tomjaguarpaw / bluefin

MIT License
52 stars 6 forks source link

Add support for custom handles #14

Open Lysxia opened 4 months ago

Lysxia commented 4 months ago

Based on your suggestion on the cleff issue tracker https://github.com/re-xyr/cleff/issues/31

I mainly wanted to show an implementation that allows the record field selectors to be used directly, so you don't need to declare operations as separate functions.

I specialized the name of the CovariantSig class, but of course it's just one of those HFunctor elsewhere.

As I mentioned on Discourse, this is an interface that is forwards-compatible with algebraic effects while being hopefully useful on its own as a restricted form of effect handlers ("tail-resumptive"). In effectful-core, this corresponds to the Dispatch.Static module.

I tried to avoid the word "handler" (which can be too easily overloaded), instead sticking to the word "handle" as in "handle-pattern".

tomjaguarpaw commented 4 months ago

Ah, this is very interesting. I am looking for ways that we can make dynamic effect definition easier. Could you add an example to confirm this works when the handler is implemented in terms of another effect? For example, define the MyReader effect in terms of State (it just gets the current value of the State)? (I appreciate that's not really a "reader" any more, but for the sake of argument ... .)

tomjaguarpaw commented 4 months ago

Could you add an example to confirm this works when the handler is implemented in terms of another effect?

In fact, if you can demonstrate runFileSystemPure using this technique, that would be ideal.

tomjaguarpaw commented 4 months ago

Here's what it looks like to write a handler with more than one operation in the context of more than one other handler. We'll need an ergonomic way of dealing with repeated insertSeconds, but otherwise this looks good.

data MyReader r m =
  MkMyReader { myAsk :: m r,
               myAsk2 :: Int -> m r
             }

instance CovariantSig (MyReader r) where
  smap f h =
    MkMyReader { myAsk = f (myAsk h), myAsk2 = fmap f (myAsk2 h) }

runMyReader ::
  r ->
  (forall s0. H.Handle (MyReader r) s0 -> Eff (s0 :& s) a) ->
  Eff s ([Int], a)
runMyReader r k =
  evalState r $ \st -> do
    yieldToList $ \y -> do
      with
        (MkMyReader { myAsk = get st,
                      myAsk2 = \i -> yield y i *> get st })
        (\rr -> insertSecond (insertSecond (k rr)))
arybczak commented 4 months ago

local is missing.

tomjaguarpaw commented 4 months ago

Let's continue the discussion about higher-order effects at https://github.com/tomjaguarpaw/bluefin/issues/15.

tomjaguarpaw commented 4 months ago

Very nice, thanks! I'll have a bit of a play, implement some other example with it, and rewrite Bluefin.Compound.

tomjaguarpaw commented 4 months ago

(I took the liberty of rebasing on master and fixing a conflict.)

tomjaguarpaw commented 4 months ago

I'm still waiting for a time when I can look at this properly, but it's one of my highest priority items.

tomjaguarpaw commented 3 months ago

Wow, I found a very nice way of defining this style of handler within any level of enclosing handlers! (within). I think this is very close to becoming the "official" way of creating new effects. Before that I need to confirm it's compatible with https://github.com/tomjaguarpaw/bluefin/issues/17.

(and rebased on master)