Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.
Newbie question. I can't believe I'm still struggling to grapple MTL stuff. The lucid library has HtmlT m a which has a MonadState instance. But how exactly do you use it? I tried flip runState mempty someFunc where someFunc is MonadState String m => HtmlT m () but that doesn't compile.
HtmlT m a
flip runState mempty someFunc
MonadState String m => HtmlT m ()
StateT String (HtmlT Identity) () seems one step forward, but then you have to lift all UI elements, thus losing access to state.
StateT String (HtmlT Identity) ()
Aha, flip runStateT mempty $ evalHtmlT someFunc works.
flip runStateT mempty $ evalHtmlT someFunc
Grr, but that doesn't render the Html at runtime
Okay, after reading the source ... unpack and pack it back: HtmlT $ fmap fst $ flip runStateT mempty $ runHtmlT someFunc
HtmlT $ fmap fst $ flip runStateT mempty $ runHtmlT someFunc
you're doing something cray here!
monad transformers should only need to be unpeeled in main
flip runState myState $ runHtmlT myHTMLFUNCTION
the State here is global
if you want local state semantic, you want to build a StateT s Html instead
StateT s Html
With StateT s Html you would have lift when rendering UI, but at that point - access to state is lost.
And it seems that the lucid library recommends putting State inside the HtmlT transformer: https://hackage.haskell.org/package/lucid-2.9.12/docs/Lucid.html#t:HtmlT
So to "inject" an inner monad, I had to peel the skin, apply the topical cream (runState), then put it back.
(well, that reads pretty gory)
Real-world code that lead to all this: https://funprog.zulipchat.com/#narrow/stream/201385-Haskell/topic/HTML.20in.20Haskell/near/188742480
Hi, I have an issue with monad transformer.
Here is the code that I wrote
collectRendu :: [String] -> IO [Rendu]
collectRendu candidate_subdir_names =
dirs <- listDirectory rootDirectory
catMaybes <$> mapM aux dirs
aux:: FilePath -> IO (Maybe Rendu)
aux dir = do
candidate <- headMaybe <$> filterM (doesRenduExists dir) candidate_subdir_names
case candidate of Nothing -> return Nothing
Just x -> Just <$> appendNewRendu dir x
doesRenduExists studentNamedDir = doesDirectoryExist . ((rootDirectory </> studentNamedDir) </>)
the part that doesn't please me is the "case candidate of...".
Candidate type is "Maybe Filepath", I want to return a "IO (Maybe Rendu)", and type of appendNewRendu is "FilePath -> FilePath -> IO Rendu".
I first used fmap until I realised that "(appendNewRendu dir) <$> candidate" will yield me a "Maybe (IO Rendu)".
I suspect there is a better way, but I'm a bit stuck. Since IO and Maybe are stacked monad I suspect there is something to do with monad transformers, but I didn't used them that much.
Asking for "(a -> m b) -> Maybe a -> m Maybe b" on Hoogle didn't yield any result
ho nvm if I correctly put parenthesis it works
Don't bother with transformers just to remove a little bit of boilerplate in single function, I don't think it's worth it from readability perspective
In this case, you can simply use traverse:
traverse (appendNewRendu dir) candidate
it also looks like mapM works here
traverse and mapM are the same ?
Yeah, because of laws
mapM is more of a legacy thing
out of curiosity there is a way to use lift that would work here ? I very rarely use monad transformers and still lack the intuition
newtype MaybeT m a = MaybeT (m (Maybe a))
which would probably work something like this:
aux dir = runMaybeT do
candidate <- MaybeT $ headMaybe <$> filterM (doesRenduExists dir) candidate_subdir_names
lift $ appendNewRendu dir candidate
MaybeT is not "automatic" by default ?
I mean I never need to add ReaderT or WriterT or StateT
You mean MaybeT constructor in example above?
headMaybe <$> filterM (doesRenduExists dir) candidate_subdir_names
:: IO (Maybe String)
which means that it doesn't fit m String for some m containing MaybeT
You're basically making use of fact that internal implementation of MaybeT uses Maybe - it could just as well use something different, and so compiler has no reason to coerce it into MaybeT by itself
it just seems automatic because you're usually using more complex combinators for Reader etc
ask === ReaderT pure
yes that's right