Hacker News new | ask | show | jobs
by dan-robertson 1704 days ago
I think I have a slightly better idea: the type you call Effect is like a continuation not an effect and so to print a string you have

  print :: String -> Effect () -> Effect ()
  hello () = fst (typed_callcc1 (print “Hello”) ())
And I guess the type of reading an int is:

  input_int :: () -> Effect Int -> Effect Int
But it still isn’t obvious to me. If you want that IO to be asynchronous then how will you return the Effect Int (by calling the argument with the input) from input_int? I suppose the answer is that you implement a scheduler but I can’t work out how you want the details for yielding to work.
1 comments

It is not like a continuation, it is exactly a (typed) continuation, or better, an infinite list of continuations (invoking the continuation yields, in addition to a possible value, the next continuation in the stream).

In your example of reading an int the EffectHandler and Effect are simply switched (better names are sink and source). And yes, for IO you will need a scheduler, but streams are much more straightforward.

I have reached my limit of pure functional language knowledge, but I can offer you a working implementation [1] in an imperative language.

I've actually implemented these typed continuations in c++ years ago, and I'm trying to understand how they differ from effects (aside for the whole imperative thing).

In the C++ implementation, for convenience the continuation object is replaced with the next continuation when invoked, but internally actually invoking a continuation function returns the yielded value and the next continuation as for my EffectHandeler example.

https://github.com/gpderetta/libtask/blob/master/tests/conti...