Defunctionalization: Everybody does it, nobody talks about it (2019)
blog.sigplan.org
blog.sigplan.org
So while this might be a cool technique for whole-program compilers, I think it doesn't really fit into anything that has an API boundary.
Please no.
What's your objection to it?
In such a scenario, you could apply defunctionalization. But you'd need to re-apply this technique whenever a new filter comes up or one becomes obsolete. Then you would have to make sure that both your client and server have the same version of the API.
If you design a functional system instead, say by allowing filters in a domain-specific language, ala jq, you write your filter function once and never need to touch the API, because it is "complete".
Which alternative do you prefer?
It depends. How hard it is to write the generalized functional system and how many filters do I need to support initially? How much do filters really change? The "but what if you need to change something" objection comes up quite a lot. In practice it is often better to apply the knowledge and requirements I have and evolve the system as my understanding changes than to try to anticipate all possible scenarios up front. In the latter case, if I put a lot of effort into something and I make the wrong call I either don't have as many changes as I thought, so it's wasted effort, or changes happen in ways I didn't anticipate, so my solution activity works against me.
The article mentions:
> remote procedure calls with higher-order functional arguments would require serializing those functions, which is not easy to do safely or efficiently.
This is exactly what Unison is designed to do. Code is not stored as text, but is stored in a serialized AST form and identified by hash. Therefore, transmission between nodes is trivial. The language is still a work in progress, but it's making rapid strides.
Here's an explanation: https://www.youtube.com/watch?v=gCWtkvDQ2ZI
The part relevant to distributed computation is at 30:08, but the earlier parts go through the basics of how code is stored, and why this design choice was made, which might help with understanding what's going on.
The reason... I like seeing how different languages handle asynchronous code vs synchronous code. F# has their way, C# has theirs, JavaScript async/await, colored functions, Project Loom, co-routines, React Fibers, etc. I'm intrigued that a language that has built-in algebraic effects can do async wihtout any other changes to the language (as if async and sync were the same).
Is there any relation to defunctionalization for this kind of stuff?
Defunctionalization: Everybody Does It, Nobody Talks About It - https://news.ycombinator.com/item?id=21916774 - Dec 2019 (20 comments)
It's as if you gave Intel your program and they produced a special optimized processor that runs just the instructions found in your program and nothing else.
> the insight of defunctionalization is to find all actual uses of the higher-order function
> Each distinct use of the filter function yields a new case in the Filter datatype
[1] "Definitional Interpreters for Higher-Order Programming Languages", John C. Reynolds, 1972 as per https://en.wikipedia.org/wiki/Defunctionalization
[2] "The GRIN project: A highly optimising back end for lazy functional languages", Urban Boquist, Thomas Johnsson, 2005
+
"A modern look at GRIN, an optimizing functional language back end", Péter Dávid Podlovics, Csaba Hruska, Andor Pénzes, 2019
Philip Wadler had this to say about the paper: "Certain papers change your life. McCarthy's 'Recursive Functions of Symbolic Expressions and their Computation by Machine (Part I)' (1960) changed mine, and so did Landin's 'The Next 700 Programming Languages' (1966). And I remember the moment, halfway through my graduate career, when Guy Steele handed me Reynolds's 'Definitional Interpreters for Higher-Order Programming Languages' (1972)."[2] This is how I discovered it I believe.
The paper is exceedingly approachable. It was so well written that I immediately purchased a used copy of Reynolds' book on programming languages (which I did not have as easy time with compared to the paper - and still remains unfinished on my bookshelf).
When they republished the paper in 1998, Reynolds wrote about how the paper came to be [3], and I believe about the discoveries of continuations [4].
I recently implemented Reynold's meta-circular interpreter in TypeScript and serialized the abstract syntax into JSON. Coincidentally, a few days later I saw a post on HN something about "executable JSON" or some such "programming language" that the creator was very proud of making it into a product of sorts. (found it... JSON Logic: https://news.ycombinator.com/item?id=27306263). Queue Greenspun's tenth rule. I chuckled as I looked at the JSON Logic syntax knowing that a little Reynold's interpreter with its AST serialized to JSON is infinitely more powerful and extensible (allowing higher-order functions and such). I highly recommend anyone reading this to write the 50 or so lines of TypeScript necessary to implement Reynold's meta-circular interpreter (EXTREMELY EASY and nearly identical line-for-line to the 1998 paper, only in TypeScript instead of lambda calculus).
Good stuff.
[0] [PDF] https://surface.syr.edu/cgi/viewcontent.cgi?article=1012&con...
[1] [PDF] https://homepages.inf.ed.ac.uk/wadler/papers/papers-we-love/...
[2] https://homepages.inf.ed.ac.uk/wadler/topics/history.html#de...
[3] [PDF] https://homepages.inf.ed.ac.uk/wadler/papers/papers-we-love/...
[4] [PDF] https://homepages.inf.ed.ac.uk/wadler/papers/papers-we-love/...
A few systems, such as the MFlow and jmacro-rpc frameworks, have tried to circumvent this problem by sending continuations over the wire in a different way: by sending over information about the execution’s past, so that the other machine may replay a function call in order to get to where the first machine left off. This can be seen as an application of my ICFP 2018 paper on thermometer continuations."