Reputation: 271
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.
Upvotes: 4
Views: 143
Reputation: 152927
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.)
Upvotes: 7