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

MTL interop #5

Open
arybczak opened this issue Apr 12, 2024 · 8 comments
Open

MTL interop #5

arybczak opened this issue Apr 12, 2024 · 8 comments

Comments

@arybczak
Copy link

arybczak commented Apr 12, 2024

Looks like you can't do seamless (i.e. without newtypes) MTL style effects interop, as already evidenced by how Eff can't have a MonadIO instance?

@tomjaguarpaw
Copy link
Owner

That's right, you can't give Bluefin's Eff a MonadWhatever instance, because the effect is at the value level rather than the constraint level.

@tomjaguarpaw
Copy link
Owner

This approach of passing in multiple effects seems not entirely terrible:

exampleMTL ::
(e1 :> es, e2 :> es, e3 :> es) =>
IOE e1 ->
Exception String e2 ->
State Int e3 ->
Eff es r
exampleMTL ioe ex st =
runMTLStyle $
handleMTLWith ioe $
handleMTLWith ex $
handleMTLWith st $ do
setWhateverTo 0
incrementWhatever
incrementWhatever
i <- getWhatever
liftIO (putStrLn $ "Whatever was " ++ show i ++ ". Now I will fail:")
fail "Failed"

@tomjaguarpaw
Copy link
Owner

Looks like you can't do seamless (i.e. without newtypes) MTL style effects interop

So, for a better answer to your question, you can do MTL style effects interop without (defining your own) newtypes. Whether that's "seamless" or not is up for debate.

@arybczak
Copy link
Author

Considering that in the "real world" code you will have interleaving of MTL style functions and "native" effects, looks like it's going to be a pain with this approach 🤔

@tomjaguarpaw
Copy link
Owner

in the "real world" code you will have interleaving of MTL style functions and "native" effects

By '"native" Effects' do you mean Eff? If so, that's fine too (see liftEffStack):

exampleMTL ioe ex st y =
runMTLStyle $
handleMTLWith ioe $
handleMTLWith ex $
handleMTLWith st $ do
setWhateverTo 0
incrementWhatever
incrementWhatever
i <- getWhatever
when (i >= 100) $
liftEffStack (yield y i)
liftIO (putStrLn $ "Whatever was " ++ show i ++ ". Now I will fail:")
fail "Failed"

I'm not going to say this is the most ergonomic thing I've ever seen, but considering it was the first thing I thought of and I worked on it for about an hour, I think it's promising.

@tomjaguarpaw
Copy link
Owner

You could also apply the conversion to individual actions to get the back into Eff immediately. I don't know what's going to work out the most ergonomically in practice. In this example everything is done in Eff so the yield doesn't need to be lifted in any way.

exampleMTL2 ::
(e1 :> es, e2 :> es, e3 :> es, e4 :> es) =>
IOE e1 ->
Exception String e2 ->
State Int e3 ->
Stream Int e4 ->
Eff es r
exampleMTL2 ioe ex st y = do
mtl (setWhateverTo 0) st
mtl incrementWhatever st
mtl incrementWhatever st
i <- mtl getWhatever st
when (i >= 100) $
yield y i
mtl (liftIO (putStrLn $ "Whatever was " ++ show i ++ ". Now I will fail:")) ioe
mtl (fail "Failed") ex

@arybczak
Copy link
Author

arybczak commented Apr 24, 2024

What if you want to call a function f :: (MonadA m, MonadB m) => m () etc. for more classes?

@tomjaguarpaw
Copy link
Owner

That one needs the following form (because mtl is just runMTLStyle fused with handleMTLWith):

runMTLStyle $ handleMTLWith aHandle $ handleMTLWith bHandle $ f

Maybe this form, or some infix operator, would be more ergonomic? It remains to be seen.

runMTLStyle (f `handleMTLWith` aHandle `handleMTLWith` bHandle)

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

No branches or pull requests

2 participants