As the type allowed in this statement

Reading "Write yourself a schema in 48 hours" and I am confused on this page https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Adding_Variables_and_Assignment :

getVar :: Env -> String -> IOThrowsError LispVal
getVar envRef var  =  do env <- liftIO $ readIORef envRef
                         maybe (throwError $ UnboundVar "Getting an unbound variable" var)
                               (liftIO . readIORef)
                               (lookup var env)

      

I don't quite understand how this type is resolved. Here's my reasoning:

envRef

has a type IORef [(String, IORef LispVal)]

, therefore readIORef envRef

has a type IO [(String, IORef LispVal)]

.

Now LiftIO

defined at http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Control-Monad-IO-Class.html as a type type liftIO :: IO a -> m a

and implemented at https://hackage.haskell.org/package/ mtl-1.1.0.2 / docs / src / Control-Monad-Error.html # ErrorT . So it liftIO $ readIORef envRef

returns m [(String, IORef LispVal)]

m for some monad (which doesn't matter, because we just use <-

on it right away, so we ignore the monad wrapper) [1].

This means env [(String, IORef LispVal)]

, therefore lookup var env

- Maybe IORefLispVal

. For the Just branch, it may liftIO . readIORef

be m LispVal

, again for some monad m. Considering that the whole function returns IOThrowsError LispVal

(it's just ErrorT LispError IO LispVal

ie IO Either LispError LispVal

), so m should be IOThrowsError

[2].

Now, if different monad transformers exist, my guess is that there could be more than one elevator in the area with a different type signature. Is this true, and if so, to what extent is the line of reasoning above representative of how Haskell defines types? I am not happy with the reasoning in [1] or [2], so there is a secondary question about how the lifting monad is defined. And finally, for the do statement, how do you know what a monad is? Is it determined by the first monad after <-

? Or in some other way?

+3


source to share


1 answer


If you have liftIO $ readIORef envRef

, with type MonadIO m => m [(String, IORef LispVal)]

, the question m

matters! It can only be a monad that implements a class MonadIO

and cannot be simply ignored. It must be the same monad that the operator returns maybe ...

. In general, when working with musical notation, all statements within a given block must have the same monad, so you could do

main :: IO ()
main = do
    putStrLn "Testing"
    x <- getLine
    putStrLn x

      

But you cannot do

main :: IO ()
main = do
    putStrLn "Testing"
    x <- Just "Doesn't work!"
    putStrLn x

      



Because IO

and Maybe

not in the same Monad! It also main

states that it is of type IO ()

, so you may not be able to return anything other than an action IO

.

If you don't know which type can be genericized too, I would recommend loading it into GHCi and checking it out without the specified type signature. I believe it will turn out to be something like

getVar :: (Eq a, MonadIO m, MonadError LispVal m) => IORef [(a, IORef LispVal)] -> a -> m b

      

because liftIO $ readIORef envRef

u liftIO . readIORef

means the monad must be an instance MonadIO

, to execute lookup

you need your keys to be an instance Eq

, and throwError $ UnboundVar "..."

> means it must be an instance MonadError LispVal

. There is nothing that makes the rest of the signature return LispVal

s or be the specific monad you are using IOThrowsError

.

+4


source







All Articles