Home > Mobile >  Can some one explain how to use lift in haskell?
Can some one explain how to use lift in haskell?

Time:07-18

I try to understand the lifting principle by example, and found this:

https://github.com/graninas/Functional-Design-and-Architecture/blob/1736abc16d3e4917fc466010dcc182746af2fd0e/First-Edition/BookSamples/CH03/MonadStack.hs

Then, if I change it's lift (lift (putStrLn "bla-bla")) to putStrLn "bla-bla", the compiler throw error!

I do this base on my understanding: do block are just syntax sugar, and each line's result are pass into next line. If the next line do not use the args that passed from previous line, the type of args won't cause type confliction, I think.

Take bellow as example, while x<- getLine can past compiling

test:: IO ()
test = do
  x <- getLine              -- discarded and compiler don't care it type 
  let a = "bla-bla"   -- discarded and compiler don't care it type 
  putStrLn $ "You type are discarded: "

Now back to the calculations function:

type Data = Map.Map Int String
type StateIO = StateT Data IO
type MaybeStateIO a = MaybeT StateIO a

calculations :: MaybeStateIO ()
calculations = do
    lift (lift (putStrLn "bla-bla")) -- if I change this to `putStrLn "bla-bla"`, it failed compiling.
    lift (modify (Map.insert 3 "3"))
    lift (modify (Map.insert 1 "1"))
    mb <- lift (get >>= (return . Map.lookup 1))
    lift (lift (print mb))

main = runStateT (runMaybeT calculations) Map.empty

I don't understand is that to compiler require lifting on putStrLn "bla-bla".

Isn't it enough when return value of the last line of do block match the function's return value?

In this example, how does the compiler decide the do block's value type? according to function's signature?

Can anyone explains the lift for me? How do it work, when to use, etc...

Thanks.

CodePudding user response:

The value of calculations is a MaybeStateIO value. That's the monad you are operating in, so that's what every line of the do block has to produce. But putStrLn "bla-bla" does not produce a MaybeStateIO value; it just produces an IO value. The first lift takes that IO value and returns a StateIO value; the second lift takes that StateIO value and returns a MaybeStateIO value.

Remember,

do
   a
   b

is just syntactic sugar for a >> b, and (>>) :: Monad m => m a -> m b -> m b needs values from the same monad as arguments. It's only the "return value" (a and b) of the monad that can vary from line to line; the monad m itself is fixed.

CodePudding user response:

Isn't it enough when return value of the last line of do block match the function's return value?

No, since that would mean that you could write a do block where the first item for example would use the instance of [] for Monad whereas the next would use for example Maybe or IO, but then how would x <- some_list make sense for a list of putStrLn x? All lines in the do block should be of type m a with m the same instance of Monad, and the as can have different types for each line. If you write a do block with:

foo = do
    x <- exp1
    exp2

then this is translated to exp1 >>= \x -> exp2, and since (>>=) :: Monad m => m a -> (a -> m b) -> m b operates where the two operands share the same monad m, this thus means that exp1 :: m a and exp2 :: m b thus need to work with the same monadic type m.

You require to perform lifting twice since the line should have as type MaybeT (StateT Data IO) a whereas putStrLn "bla-bla" has IO a, it thus requires one lift :: (MonadTrans t, Monad m) => m a -> t m a to lift it to StateT Data IO a and another to finally lift it to a MaybeT (StateT Data IO) a.

  • Related