I'm trying to build a way to transform functions of type (with any number of arguments):
Monad f => env -> f z
Monad f => env -> a -> f z
Monad f => env -> a -> b -> f z
into:
MonadReader env m => m z
MonadReader env m => a -> m z
MonadReader env m => a -> b -> m z
So I've created the following class (with helper closed type families):
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.Reader
import Data.Kind
(&>) :: Functor f => f (p -> b) -> p -> f b
ff &> x = ff <&> ($ x)
infixl 4 &>
-- Environment type put into the `MonadReader`
type family RdType a where
RdType (r -> a) = r
-- Function type after chopping off first argument. It distinguishes every case,
-- because we need access to `m` type.
type family RetType a where
RetType (r -> a -> b -> c -> m d) = a -> b -> c -> m d
RetType (r -> a -> b -> m c) = a -> b -> m c
RetType (r -> a -> m b) = a -> m b
RetType (r -> m a) = m a
-- Class trying to capture functions with any number of arguments
class (MonadReader (RdType a) m, RetType a ~ b) => Unwrap a b m | a b -> m where
unwrap :: a -> b
-- INSTANCE #1
-- instance (RetType (r -> a -> m b) ~ (a -> m b), MonadReader r m) => Unwrap (r -> a -> m b) (a -> m b) m where
-- unwrap f a = join $ asks f &> a
-- INSTANCE #2
instance (RetType (r -> a -> b -> m c) ~ (a -> b -> m c), MonadReader r m) => Unwrap (r -> a -> b -> m c) (a -> b -> m c) m where
unwrap f a b = join $ asks f &> a &> b
However when I uncomment instance #1 and try to use it it results in an error like below:
data SomeEnv
f1 :: Functor m => SomeEnv -> Int -> String -> m ()
f1 = undefined
-- uncommenting INSTANCE #1 results in error here
--
-- • Couldn't match type ‘[Char]’ with ‘SomeEnv’
-- arising from a functional dependency between:
-- constraint ‘MonadReader SomeEnv ((->) String)’
-- arising from a use of ‘unwrap’
-- instance ‘MonadReader r ((->) r)’ at <no location info>
-- • In the expression: unwrap f1 1 "asdf"
-- In an equation for ‘x’: x = unwrap f1 1 "asdf"
x :: MonadReader SomeEnv m => m ()
x = unwrap f1 1 "asdf"
It appears that instance #1 with RetType _ ~ (a -> m b) captures also a -> b -> m c case (covered by instance #2), which breaks everything.
An example for more context how unwrap can be used:
data Env m = Env { logger :: Db m }
data Logger m = Logger { flush :: m (), logAt :: Int -> String -> m () }
type AppEnv = Env App
newtype App a = App { unApp :: ReaderT AppEnv IO a }
deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv)
myApp :: App ()
myApp = do
logInfo "fooo"
unwrap (flush . logger)
logInfo :: MonadReader AppEnv m => String -> m ()
logInfo txt = (logAt . logger) `unwrap` 1 txt
Is it possible to distinguish those two cases? Is it even possible to create such class?