3 ms·
So I think this is a good introduction to what algebraic effects are but I think it also misses the mark a bit. The one sentence summary I would use for algebr
by dan-robertson 6y ago
So I think this is a good introduction to what algebraic effects are but I think it also misses the mark a bit.
The one sentence summary I would use for algebraic effects is “they’re the way to add delimited continuations to a statically typed language.” Similarly, one might say that ML/ocaml/haskell style polymorphism [excluding typeclasses] is the way to take functions like map or filter from dynamically typed to statically typed languages.
I guess delimited continuations are an ok model for how react hooks work except that (as far as I’m aware) nothing continuation-like is involved. Instead one might think of them as scheduling work to be done very soon (in some sense) after your function runs.
An alternative question is: how do you type something like hooks? I think this mostly boils down to: how do you enforce the rules of hook usage with types? The rules are that: 1. Only certain functions may use hooks, 2. Any function which uses hooks must always create hooks (including hooks created by called functions) in exactly the same order every time it is run.
Algebraic effects aren’t really suitable for enforcing this rule (the OP has an example of state effects but there is only one state. It misses the killer feature of hooks which is to say in a relatively ad-hoc way, “I need a bit of state, please let me use some”. Instead you need to add an effect and handler for every bit of state or thread through access to a single bit of state (or have code where one must manually ensure that the rules are followed, eg a “useState” effect)).
I tried writing such a thing in ocaml about a year ago. I used a monadic interface with a second type parameter tracking the types of the states which are created. But this isn’t sufficient as you can still write rule-breaking code like:
function
| true -> map (useState 1) ...
| false -> map (useState 2) ...
You need to ensure that the type parameter tracking the calls is always abstract in such a way that the compiler will never determine that it is equal to a different useState. But I didn’t think of a way to do that and stopped looking.
- sfvisser 6y agoI think hooks are more like applicative/arrow and are not monads. This ensures the shape is static and you cannot branch based on input. This also illustrates how weird it is to call function components pure. They’re not, there as effecful as can be!
- dan-robertson 6y agoYeah I agree that morally the interface is applicative or arrow like but I didn’t think that provided an ergonomic programming interface which is most of the point of hooks (and the pain of arrows). Binds couldn’t really changed the shape of their output because of the second type parameter I described. Also, I don’t see a particular reason to disallow the default value of one state depending on the value of something else. You can’t really stop them from depending on eg function arguments in a nice way.
- sweetsocks21 6y agoIf you haven't seen the Bonsai OCaml library, they provide an arrow based interface for making web apps. I find it very straight forward to use and everything is based around composing functions. A "component" is: type ('a, 'b) t = 'a Value.t -> 'b Computation.t and then there's various ways to hook them up and depend on each other. https://github.com/janestreet/bonsai/blob/master/docs/proc.md https://github.com/janestreet/bonsai/blob/master/docs/proc.m... Gives a bit of an overview. There are some syntax extensions to make working with it a bit easier. But once grokked it seems to fit nicely, and feels like more of a solid foundation than React (imo). Not all the examples have been updated to the new style, but here's one (albeit trivial) example: https://github.com/janestreet/bonsai/blob/master/examples/two_instances_of_component/main.ml https://github.com/janestreet/bonsai/blob/master/examples/tw...