Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some more interpretation functions #230

Closed
wants to merge 1 commit into from

Conversation

michaelpj
Copy link
Contributor

This adds some more interpretation functions that are useful for

  • Postfix handling of effects
  • Handling of first-order effects that don't use the environment

We have these defined in our local codebase and they're quite nice.

  • We found that very many cases do not need the environment (as they are not "higher-order"), and it's nice to be able to omit it.
  • The handle style is nice for effects that don't have a global interpreter but are interpreted specifically at the use site.

As an example of these together, we sometimes use effectful to as a fancy way of defining callbacks for a function, like so:

data Event :: Effect where
  NotifyA :: Int -> Event m ()
  NotifyB :: String -> Event m ()

makeEffect ''Event

myFunction :: (Event :> es) => Arg -> Eff es ()

myFunction arg `handle_` $ \case
  NotifyA i -> ...
  NotifyB s -> ...

-- alternatives today
myFunctionArg `flip interpret` $ \_ -> \case
  NotifyA i -> ...
  NotifyB s -> ...

interpret handler $ myFunction arg
  where
    handler _ = \case
      NotifyA i -> ...
      NotifyB s -> ...

This adds some more interpretation functions that are useful for
- Postfix handling of effects
- Handling of first-order effects that don't use the environment
@arybczak
Copy link
Member

arybczak commented Aug 7, 2024

Since the suffix _ is quite overloaded, perhaps interpret1 etc. would be better? It somewhat works since these are for first order effects. I'm not 100% sure though 🤔 Thoughts?

handle is in theory nice, but it clashes with Control.Exception.handle/Control.Monad.Catch.handle 😞 Do you know you can write

(`interpret` myFunction arg) $ \_ -> \case
  NotifyA i -> ...
  NotifyB s -> ...

? Maybe it's enough to add demonstration of this syntax to haddock of these functions?

@michaelpj
Copy link
Contributor Author

Since the suffix _ is quite overloaded, perhaps interpret1 etc. would be better? It somewhat works since these are for first order effects. I'm not 100% sure though 🤔 Thoughts?

Yeah, I agree that _ isn't great, especially since the usual interpretation of that is "discards its result", which isn't what's happening here. The 1 suffix also usually connotes "works on non-empty structures", which also doesn't seem quite right. If we were starting from scratch I'd probably argue to call this interpret and the existing one interpretHigherOrder... How about interpretSimple? interpretGlobal since it doesn't use the local environment? interpret' 😅 ?

handle is in theory nice, but it clashes with Control.Exception.handle/Control.Monad.Catch.handle 😞

That's true, I don't know how much you care that people would need to use this qualified. I do think it's a pretty natural name: people talk about "effect handlers" all the time, I think you have a good claim to it. I would rather have the good name and have to use it qualified, personally.

Do you know you can write

I could do that, yeah. But I really don't want to. I think that's much worse for readers of my code than the version with handle_. I would just continue using my local handle_ in preference. If you don't think offering the convenient version from effectful-core is worth it that's fine too.

@ocharles
Copy link

ocharles commented Aug 7, 2024

Could change handle to handling? It seems only lens stuff is really claiming that, which feels fair to share with

@ocharles
Copy link

ocharles commented Aug 7, 2024

I also don't think _ is awful here. Yes it's a bit different to what for_ does, but you can just say that the _ means the _ you'd write in pattern matching. Too much of a stretch? 😅

@arybczak
Copy link
Member

arybczak commented Aug 7, 2024

I guess _ is alright.

That's true, I don't know how much you care that people would need to use this qualified.

I tried hard to make the API not clash with typically used modules/libraries, so it would be a shame to lose this.

How about interpretWith instead of handle? It reads nicely in the infix version:

myFunction arg `interpretWith_` $ \case
  NotifyA i -> ...
  NotifyB s -> ...

@ocharles
Copy link

ocharles commented Aug 7, 2024

I like that!

@michaelpj
Copy link
Contributor Author

Closing in favour of #231

@michaelpj michaelpj closed this Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants