Hello! I would like to have a Reader which gives me multiple functions. For example, if I have two possible databases, it does not make sense to read from database A and delete from the other.
Here is a made up example with 1 function
initA :: String -> Effect Unit
initA param = log $ "init A " <> param
main :: Effect Unit
main = runReader go initA
go :: ReaderT (String -> Effect Unit) Identity (Effect Unit)
go = do
result <- ask
pure $ result "works"
“init A works”
2 functions:
getFromA :: String -> Effect String
getFromA param = pure $ "from A " <> param
initB :: String -> Effect Unit
initB param = log $ "init B " <> param
getFromB :: String -> Effect String
getFromB param = pure $ "from B " <> param
type System
= { init :: String -> Effect Unit, get :: String -> Effect String }
systemA :: System
systemA = { init: initA, get: getFromA }
systemB :: System
systemB = { init: initB, get: getFromB }
main' :: Effect Unit
main' = (runReader go' systemA) >>= log
go' :: ReaderT (System) Identity (Effect String)
go' = do
result <- ask -- <-Error
result.init "works"
pure $ result.get "works"
Could not match type Effect with type ReaderT { get :: String -> Effect String, init :: String -> Effect Unit} Identity
Can somebody explain why the first one works, the last one doesn’t?
works and the Reader solution should look and behave similar. Switching to runReaderT, changing the transformer stack and lifting every function does the trick.
If liftEffect works only with Effect and lift works with every transformer, I should stick with lift.
Lifting do to see different levels is nice. I have tried it with a longer stack:
go' :: ReaderT System (ExceptT String Effect) String
go' = do
result <- ask
lift $ do
s1 <- lift $ do
result.init "works"
result.get "one"
result.getEE $ s1
Unfortunately purty changes it:
go' :: ReaderT System (ExceptT String Effect) String
go' = do
result <- ask
lift
$ do
s1 <-
lift
$ do
result.init "works"
result.get "one"
result.getEE $ s1
Thanks a lot. I realized if I make a mistake at calling the asked functions, it is hard to spot the error because it only points to the line of the ask statement.
I have tried to make a new monad with newtype and keep ReaderT only for config values. I have integrated ExceptT because I would like to use simple functions (maybe with effects) which return either error string or success something. They should be combined like in a try … catch block and go to catch at the first Left.
type Env
= { url :: String
}
newtype AppM a
= AppM (ReaderT Env (ExceptT String Effect) a)
runAppM :: Env -> AppM String -> Effect Unit
--runAppM :: Env -> AppM ~> Effect --<-- Not sure about that!
runAppM env (AppM m) =
runExceptT (runReaderT m env)
>>= case _ of
Left e -> log e
Right a -> log a
derive newtype instance functorAppM :: Functor AppM
derive newtype instance applyAppM :: Apply AppM
derive newtype instance applicativeAppM :: Applicative AppM
derive newtype instance bindAppM :: Bind AppM
derive newtype instance monadAppM :: Monad AppM
derive newtype instance monadEffectAppM :: MonadEffect AppM
class
Monad m <= ManageUser m where
init :: String -> m Unit
get :: String -> m String
getEE :: String -> m String
instance manageUserAppM :: ManageUser AppM where
init param = liftEffect $ log $ "init A " <> param
get param = pure $ "from A " <> param
getEE input = lift $ except $ if input == "from A http://foo" then Right "success" else Left "failure"
--<--Error Could not match type t0 (ExceptT String t3) with type AppM
instance monadAskAppM :: TypeEquals e Env => MonadAsk e AppM where
ask = AppM $ asks from
program ::
forall m a.
MonadAsk Env m =>
ManageUser m =>
m a
program = do
config <- ask
init config.url
s1 <- get "one"
s2 <- getEE s1
getEE ("fail please" <> s2)
lift $ lift $ log "no no no"
I don’t understand the problem with the getEE line. if ... returns an Either, except returns ExceptT, to complete AppM the ReaderT is missing, which I get with lift.