I implemented a simple state machine in Python:
import time
def a():
print \"a()\"
return b
def b():
print \"b()\"
return c
def c():
print
An example in F#:
type Cont = Cont of (unit -> Cont) let rec a() = printfn "a()" Cont (fun () -> b 42) and b n = printfn "b(%d)" n Cont c and c() = printfn "c()" Cont a let rec run (Cont f) = let f = f() run f run (Cont a)
Regarding the question "why is it so hard to implement state machines using functions in statically typed languages?": That's because the type of of a
and friends is a little bit weird: a function that when returns a function that returns a function that returns a function...
If I remove Cont from my example the F# compiler complains and says:
Expecting 'a but given unit -> 'a. The resulting type would be infinite when unifying 'a and unit -> 'a.
Another answer shows a solution in OCaml whose type inference is strong enough to remove the need for declaring Cont, which shows static typing is not to blame, rather the lack of powerful type inference in many statically typed languages.
I don't know why F# doesn't do it, I would guess maybe this would make the type inference algorithm more complicated, slower, or "too powerful" (it could manage to infer the type of incorrectly typed expressions, failing at a later point giving error messages that are hard to understand).
Note that the Python example you gave isn't really safe. In my example, b
represents a family of states parameterized by an integer. In an untyped language, it's easy to make a mistake and return b
or b 42
instead of the correct lambda and miss that mistake until the code is executed.