Haskell: государственная монада

Я пытаюсь изучить Хаскель. Я написал код, который использует глобальное состояние и может изменять его с помощью двух функций (т. е. change1 и change 2). Кроме того, я включил объектив, чтобы я тоже мог его изучить. Проблема в самой последней строке кода. Я не понимаю, почему я не могу включить две последние строки вместе в main. Он будет работать, если я удалю любой из них.

{-# LANGUAGE TemplateHaskell #-}

module Dd (main, ma, change1,change2, Dp(Dp))  where
import Control.Lens
import Control.Monad.State
import Control.Monad.IO.Class (liftIO)

data Dp = Dp {_sr  :: Int , _fl :: Int} deriving (Show)
makeLenses ''Dp

plus ::  Dp  -> Dp
plus  = over (sr) (+90)

mult4 ::  Dp  -> Dp
mult4 = over (fl) (*100)

change1 :: State Dp ()
change1 = modify plus

change2 :: State Dp () 
change2  = modify  mult4 

ma :: State Dp ()
ma  = do 
      change1
      change2 

main = do 
     runState ma (Dp 2 3)
     evalState ma (Dp 2 3)-- here is the problem

Редактировать: я немного смущен: почему приведенный ниже код работает без ошибок?

data Dp = Dp {_sr  :: Int , _fl :: Int} deriving (Show)

makeLenses ''Dp
gs :: StateT Dp IO Int
gs = do 
d <- gets _sr
liftIO $ print (d)
dd<- uses sr (10<=)
if (dd) then 
            return (10)
            else return (90)
 main  =  do 
        runStateT gs (Dp 3 6)
        evalStateT gs (Dp 3 6)

person 4xx    schedule 25.07.2017    source источник
comment
runState возвращает (a, s), что означает, что ваш main использует монаду (,) a. evalState не возвращает значение в той же монаде, поэтому они не могут быть составлены с использованием нотации do.   -  person 4castle    schedule 25.07.2017
comment
Добавление аннотации типа к main может улучшить сообщения об ошибках. Вы, вероятно, захотите print результаты, чтобы проверить все типы.   -  person chi    schedule 25.07.2017
comment
Я оспариваю утверждение, что он будет работать, если я удалю любой из них. Вы проверяли это? Когда я пытаюсь это сделать, я получаю жалобу, что main не имеет типа IO.   -  person Daniel Wagner    schedule 26.07.2017
comment
@DanielWagner Он не жалуется на меня, если загружается с помощью GHCi   -  person 4castle    schedule 26.07.2017
comment
runState возвращает (a, s), а runStateT возвращает m (a, s), то есть IO (a, s) здесь. Аналогично IO a для evalSTateT. Теперь оба IO ..., так что все в порядке. Это по-прежнему не будет выполнять никаких операций ввода-вывода, просто вернет последнее целое число. Вы можете увидеть типы в GHCi с помощью команды :t в ее приглашении (например, :t runStateT).   -  person Will Ness    schedule 26.07.2017
comment
@DanielWagner спасибо за ваш комментарий. Да, я проверял, и именно поэтому я сказал, что он заработает, если я удалю один из них. Я не знаю, почему он скомпилирован на m-машине!   -  person 4xx    schedule 26.07.2017


Ответы (1)


В вашем случае do-notation - это синтаксический сахар для

(runState ma (Dp 2 3)) >> (evalState ma (Dp 2 3))

Тип (>>)Monad m => m a -> m b -> m b, что означает, что runState ma (Dp 2 3) и evalState ma (Dp 2 3) должны быть в одной и той же монаде, но они не

Но код не работает в основном по другой причине. Функция main имеет следующую аннотацию типа: main :: IO a что означает, что main ожидает, что в ней будет выполнено действие (например, печать результатов)

main :: IO ()
main = do
  print $ runState ma (Dp 2 3)
  print $ evalState ma (Dp 2 3)

Это работает, потому что

  • тип print $ runState ma (Dp 2 3) это IO ((), Dp)
  • тип print $ evalState ma (Dp 2 3) это IO ()

Следовательно, предполагаемый тип для >> — это IO ((), Dp) -> IO () -> IO (), который не нарушает Monad m => m a -> m b -> m b. В результате получается тип IO (), который подходит для main :: IO a.

person Igor Drozdov    schedule 25.07.2017