I'm looking for a way to define natural transformation as a simple argument to pass to a function.
Let's have a simple function as an example:
mapK :: (m Int -> IO Int) -> Kleisli m Bool Int -> Kleisli IO Bool Int
mapK toIO kleisli = Kleisli (\bool -> toIO $ runKleisli kleisli bool)
Neat, but if I change it to:
mapK :: (m a -> IO a) -> Kleisli m Bool Int -> Kleisli IO Bool Int
mapK toIO kleisli = Kleisli (\bool -> toIO $ runKleisli kleisli bool)
I get the error that Int
isn't an a
(no surprise there).
I know it's possible to achieve it using constraint like:
class NaturalTransformation f g where
transform :: f a -> g a
mapK :: (NaturalTransformation m IO) => Kleisli m Bool Int -> Kleisli IO Bool Int
but I'm curious if it's also possible to do it in plain arguments.
Certainly, you just need to demand that the implementer, not the caller, gets to choose the variable:
{-# LANGUAGE RankNTypes #-}
mapK :: (forall a. m a -> IO a) -> Kleisli m Bool Int -> Kleisli IO Bool Int
-- implementation is exactly as before
mapK toIO kleisli = Kleisli (toIO . runKleisli kleisli)
-- OR, with TypeApplications also on,
mapK toIO = coerce @(_ (Bool -> _ Int)) (toIO.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With