That is a beautiful idea! I was originally just thinking about simplifying the syntax for nested updates, but your approach also makes the update paths composable as functions.
Would something like this make sense to you instead of %?
Then you would have to embed something like a Web Worker for parallelizing Tasks. To me, that actually fits quite naturally into Elm’s way of thinking.
During a long car ride, I discussed with ChatGPT how this could be integrated more deeply into Elm. It pointed me towards techniques such as lambda lifting and closure conversion. As I understand it, these could allow the compiler to represent certain closures as serializable data: the worker would already contain the compiled function, while only a function identifier and the captured environment would be transferred.
This could potentially also work for dynamically composed functions, as long as all participating functions are known to the compiler and all captured values can be serialized.
I had not been aware that this was possible. Claude currently refuses to implement it, arguing that functions cannot be transferred to a Web Worker in the browser.
Yes that makes sense. I like re-using the existing known syntax.
I chose % mostly arbitrarily but a little bit because per cent (pour cent) makes me think of part / whole relationships and that relates to updating a small thing in a larger thing (a focused update, a traversal optic) in a kind of vague way. So I guess I chose it because of very light association or affinity with the concept.
I like things that don’t require a Shift key so \ wins there big time over %. It also suggests division which I think is also in the mental ballpark. At first I thought that \ being the lambda introducing token would be a mark against \ because it might be an ambiguous collision for the reader but these are update functions and so perhaps the token similarity helps put readers in the right frame of mind.
By the way, on the reading or viewing side of the fence…
I’ve thought of syntax for reading values out of variants of a single argument with a syntax like .Apple which would be typed Fruit -> Maybe AppleInfo. The problem with that is that if a custom type has a single variant then it isn’t a Maybe which makes the typing contextual which is horrible for readability. So then I thought that maybe it could always return a Maybe (inconvenient for the single variant case) or that the syntax would need to differ based on the number of variants. A single variant sum type would use the . and a multiple variant sum type would use a ?. So .Posix might be Posix -> Int but it would be ?Apple for Fruit -> Maybe AppleInfo. The reason I’ve thought about this is that it seems like a completion of an idea that Elm already has. Why is there a shortcut to read fields of product types but not variant values of sum types? However, an obviously good syntax is not obvious to me.
Nesting
PureScript has the syntax _.x which is equivalent to Elm’s .x but you can nest! So _.a.b.c is an accessor of any record with an a field of a type with a b field of a type that has a c field. So then we might chain .person.contactMethod?PhoneNumberContact.isDaytime… but that ? looks ugly. .
Safe navigation. We might use the ?. operator in a chain to work with Maybe’s BUT what about Results?! If you made safe navigation contextual (works with both) you ruin readability. If you don’t you need a syntax to support safe navigation of Result which gets confusing to have two AND you realize that Result is tricky because you would need to normalize or convert the error type.
The central thrust of my consideration was something like “What if we improved the ergonomics of viewing and updating in a simple and learnable way while keeping engineers mostly working with functions rather than special syntax?”.
At the end of the day, I am actually addicted to Elm’s simplicity. Every time I think of a new syntax it fails to be elegant enough for real consideration.
Like I love functions and want everything to be functions. So I also have an extended case syntax idea of ~> which is, surprise surprise, another very special case construct (the real Elm case syntax is obviously a very robust pattern match). So the ~> works this way
VARIANT_OF_ONE_ARG x -> EXPRESSION_RESULTING_IN_FUNCTION x
Again with the convenience: It drives me nuts having to pattern match an arg just to immediately forward it to a function. BUT AGAIN I think this is a bad idea for a language feature.
I have half a dozen other terrible language ideas I won’t share. I really do love that Elm, as a language, is less than 10% as complex as TypeScript or C#.
This returns a lambda that adds some value to the input, as prepared by passing that value to f to build the lambda.
You can represent this closure as a record:
{
function : \x y -> x + y <-- Pointer to this function (or name it gets compiled to)
arg1: x <-- Filled in with say 2 from env
arg2: y <-- Empty slot
}
To evaluate this, once y is filled in, invoke the function with x and y. If it was native code, we could use a function pointer to point to the compiled x+y function, or in javascript we could compile that and give it some name “g_123” and refer to it by name.
So yes, you could pass closures as data over to a web worker.