3

чтения «Пишите себя схемой в течение 48 часов» и путается на этой странице 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) 

Я не совсем ясно, как будет решен тип. Вот мои аргументы:

envRef имеет тип IORef [(String, IORef LispVal)], поэтому readIORef envRef имеет тип IO [(String, IORef LispVal)].

Теперь LiftIO определяется в http://hackage.haskell.org/package/transformers-0.4.1.0/docs/Control-Monad-IO-Class.html как типа liftIO :: IO a -> m a и реализуется в https://hackage.haskell.org/package/mtl-1.1.0.2/docs/src/Control-Monad-Error.html#ErrorT. Итак, liftIO $ readIORef envRef возвращает m [(String, IORef LispVal)] для некоторой монады m (что не имеет значения, потому что мы просто используем <- на нем сразу, поэтому игнорируем оболочку монады) [1].

Это означает, что env [(String, IORef LispVal)], поэтому lookup var env является Maybe IORefLispVal. Для ветви Just может быть, liftIO . readIORef будет m LispVal, снова для некоторой монады m. Учитывая, что вся функция возвращает IOThrowsError LispVal (это всего лишь ErrorT LispError IO LispVal i.e. IO Either LispError LispVal), поэтому m должно быть IOThrowsError [2].

Теперь, если существуют разные трансформаторы монады, я предполагаю, что в области может быть более одного лифта с другой подписью типа. Действительно ли это так, и если да, то в какой степени цепочка рассуждений выше представителя того, как Haskell определяет типы? Я недоволен рассуждениями в [1] или [2], поэтому есть вторичный вопрос о том, как определяется монада для лифтинга. И, наконец, для заявления do, как узнать, что такое монада? Это определяется первой монадой после <-? Или каким-то другим способом?

ответ

4

Когда у вас есть liftIO $ readIORef envRef, с типом MonadIO m => m [(String, IORef LispVal)], m, безусловно, имеет значение! Это может быть только монада, которая реализует класс MonadIO, и его нельзя просто игнорировать. Это должна быть та же монада, которая была возвращена оператором maybe .... В целом, при работе с делать запись, все операторы внутри данного блока должны иметь один и тот же монады, так что вы могли бы сделать

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

Но вы не может сделать

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

Поскольку IO и Maybe не в одной монаде! Кроме того, main указан для типа IO(), поэтому у вас не может быть возможности вернуть что-то иное, кроме IO.

Если вы не знаете, какой тип может быть обобщен, я бы рекомендовал загрузить его в GHCi и проверить его без указанной сигнатуры типа. Я считаю, что это будет в конечном итоге что-то вдоль линий

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

потому liftIO $ readIORef envRef и liftIO . readIORef означает, что монада должна быть экземпляром MonadIO для того, чтобы выполнить lookup, вам нужны ваши ключи, чтобы быть экземпляром Eq, а throwError $ UnboundVar "..." означает, что он должен быть экземпляром MonadError LispVal.Нет ничего, что заставляет остальную часть подписи возвращать LispVal s или быть конкретной монадой, которую вы используете IOThrowsError.

+0

Спасибо. Таким образом, возврат предложения do должно соответствовать типу возвращаемого выражения, что означает, что оба 'liftIO' должны возвращать значения' IOThrowsError' (поскольку один из них является возвратом предложения do, а другой - <- применяется к нему). Но первый 'liftIO' работает на' IO [(String, IORef LispVal)] ', поэтому я не вижу, каким может быть его тип результата. –

+0

Тип 'liftIO' -' MonadIO m => IO a -> m a'. Поскольку ваш тип 'IOThrowsError a' - это просто' ErrorT LispVal IO a', а 'MonadIO m => ErrorT ema' - это экземпляр' MonadIO', вы можете ограничить тип 'liftIO'' 'liftIO :: IO a -> ErrorT LispVal IO a'. – bheklilr

Смежные вопросы