I’ve been using World Machine (WM) for quite a while now, probably 8 years or so, and, as with most programs, the more experience you get with it, the more you can feel limited by it. That is not to say such “feeling of limitation” means the program is per se bad, but it does let you desire certain features.
So here is a roller-coaster of thoughts. Disclaimer, for those familiar with functional programming, terminology may be used incorrectly for illustrating purposes/making it easier to read/understand).
Last year I learned functional programming (FP), a type of programming paradigm, and with FP a core concept is that a function takes an input and produces an output and does nothing else. Another core concept is that a function will, given the same input always the same output. And while using WM, a thing I started to notice is that WM is almost like a FP language, where each device is a function and, given the same input, will always give you the same output. The devices’ parameters may be open to change, but it will always have some expected behaviour.
In FP, another important concept is the Type system. A type is a category of values that are the same, like the Integer type containing all integer numbers and the Float type containing all floating point numbers. The sweetness of these types is it lets you make assumptions over the data you’ll be working with and therefore setup a chain of actions (read: functions/devices). And, who would’ve thought, WM has a type system as well! The Heightfield type, the Bitmap type, the Water type, the Transform type etc.
So… why is this important? Again, in FP, these types allow you to make assumptions and predict behaviour. You know that, when getting an Integer type, it will always be an integer (like 42), and never be some text value (like “hey”), which is called Type Safety (and right now WM is kind of “type safe”, as you cannot link a bitmap to a device that only takes in a Heightfield). FP then takes it a step further and lets you, the user, create your own types, either from scratch or build up from existing types, and you can use existing functions that work with said type with your new type, as long as they abide the laws of that type (technically these are type classes), and this is were the interesting part comes.
In WM, the Composite Type lets you unpack types. The Water System type is made out of 3 types, two being a Heightfield and one being a Vector Field type (which in itself is made out of two Heightfield types). Another example is the Bitmap type. When using the Channel Splitter device, you actually unpack the type and get 3 Heightfield types, each representing a colour channel. Once you have a Heightfield type, you can use almost any device on it to transform it.
So… why is this important? Knowing this I came up with the following propositions.
- Let users create custom types, which can be shared and added to WM kinda like extensions of the “core WM type system”.
- Let users create interpretation of types with default behaviour.
The first proposition is nice for macros as it would let you create your own type, for example a Snow type that is made out of 2 Heightfields and a Parameter type, so you can exchange that Snow type with other macros without having to hook up 3 wires but instead only 1 wire. Less wiring! Oh and because of the Type Safety you now no longer can hook up the 2 Heightfields of the Snow type incorrectly, bonus!
The second proposal basically would enable you to manipulate a Bitmap type much easier because, instead of having to use a Channel Splitter device, then use a device on all three the channels and then again use a Channel Combiner device to create a Bitmap once again, you tell WM how to handle the Bitmap type.
You create rules/laws for your type and tell that, if you connect a Bitmap type to a Clamp, the clamp device knows “Oh, I first need to unpack this type into three channels, then clamp all the three channels separately and then repack them into a Bitmap!.”, instead of always having to wire Channel Splitters and such. You simply hook it up to the clamp device and it will work, because you have told WM how to handle your type and therefore does all the wiring in the back. This will only work, of course, if you tell WM how to handle your new type and it would be quite a painful experience if you always have to do a “demonstration hookup” of your type for every device in WM. Instead, it would be better if you could “proof” your type can be interpreted and so use the default behaviour and at the same time be able to define exceptions for certain devices.
For example, maybe you want your Snow type’s heightfields to always (default) be handled the same (that is, both get processed by the device that’s taking in the Snow type), except (exception) for when hooking it up to the clamp device, then you only want one of the two heightfields to be affected. Again, all this information would be stored in the Snow type and someone using your Snow type does not need to think about it as WM does all the heavy lifting in the back. Then, if someone really wants to use a clamp on both heightfields in the Snow type, they can always use the Composite Type device to unpack your type and then use a clamp device on both heightfields.
I hope this roller-coaster was somewhat understandable. If you have any questions, remarks or thoughts, I would love to hear them and discuss!