Стопка преобразователя монады Haskell и подписи типа

Я пытаюсь создать стопку преобразователей монады и испытываю затруднения при получении корректных подписей типа для моих функций. (Я все еще довольно плохо знаком с Haskell),

Стек комбинирует несколько преобразователей StateT, так как у меня есть несколько состояний, которые я должен отслеживать (два из которых могли быть tupled, но я доберусь до этого через секунду), и WriterT для входа.

Вот то, что я имею до сих пор:

module Pass1 where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map
import Types

data Msg = Error String
         | Warning String

type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a


runPass1 addrs instrs msgs = runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)


--popLine :: (MonadState s m) => m (Maybe s)
--popLine :: (Monad m) => StateT [Line] m (Maybe Line)
popLine :: (MonadState s m) => m (Maybe Line)
popLine = do
        ls <- get
        case ls of
          x:xs -> do
                    put xs
                    return $ Just x
          []   -> return Nothing


incLineNum :: (Num s, MonadState s m) => m ()
incLineNum = do
               ln <- get
               put $ ln + 1

curLineNum :: (MonadState s m) => m s
curLineNum = do
               ln <- get
               return ln

evalr = do l <- popLine
           --incLineNum
           return l

Я хотел бы popLine смешать с [Line] состояние и xLineNum функции для влияния Int состояние. evalr вычисление, которое будет передано runPass1.

Каждый раз, когда я загружаю код, я сталкиваюсь с ошибками, которые обычно имеют следующее разнообразие:

Pass1.hs:23:14:
    No instance for (MonadState [t] m)
      arising from a use of `get' at Pass1.hs:23:14-16
    Possible fix: add an instance declaration for (MonadState [t] m)
    In a stmt of a 'do' expression: ls <- get
    In the expression:
        do ls <- get
           case ls of {
             x : xs -> do ...
             [] -> return Nothing }
    In the definition of `popLine':
        popLine = do ls <- get
                     case ls of {
                       x : xs -> ...
                       [] -> return Nothing }


Pass1.hs:22:0:
    Couldn't match expected type `s' against inferred type `[Line]'
      `s' is a rigid type variable bound by                        
          the type signature for `popLine' at Pass1.hs:21:23        
    When using functional dependencies to combine                  
      MonadState [Line] m,                                         
        arising from a use of `get' at Pass1.hs:23:14-16            
      MonadState s m,                                              
        arising from the type signature for `popLine'              
                     at Pass1.hs:(22,0)-(28,31)                     
    When generalising the type(s) for `popLine'         




Pass1.hs:23:14:
    Could not deduce (MonadState [Line] m)
      from the context (MonadState s m)   
      arising from a use of `get' at Pass1.hs:23:14-16
    Possible fix:                                    
      add (MonadState [Line] m) to the context of    
        the type signature for `popLine'             
      or add an instance declaration for (MonadState [Line] m)
    In a stmt of a 'do' expression: ls <- get
    In the expression:
        do ls <- get
           case ls of {
             x : xs -> do ...
             [] -> return Nothing }
    In the definition of `popLine':
        popLine = do ls <- get
                     case ls of {
                       x : xs -> ...
                       [] -> return Nothing }

Ни одна из подписей, кажется, не корректна, но popLine является первой функцией, таким образом, это - единственное, которое сразу вызывает ошибку.

Я пытаюсь добавить, что это предлагает в подписи типа (например: popLine :: (MonadState [Line] m) => ... но затем это ошибки как так:

Pass1.hs:21:0:
    Non type-variable argument in the constraint: MonadState [Line] m
    (Use -XFlexibleContexts to permit this)                          
    In the type signature for `popLine':                             
      popLine :: (MonadState [Line] m) => m (Maybe Line)

Я всегда, кажется, получаю это сообщение каждый раз, когда я пытаюсь сделать что-то, что не является переменной типа. Этому, кажется, нравится (MonadState s m) хорошо и ошибка на чем-то еще, но когда я пробую его a [a] вместо s это ошибки, подобные вышеупомянутому. (Первоначально [Строка] и Интервал был tupled в единственном состоянии, но я получал эту ошибку, таким образом, я думал, что попытаюсь поместить их в отдельные состояния).

GHC 6.10.4, Kubuntu

Так, кто-либо может сказать мне, что продолжается, и дайте объяснение / показывают мне правильные подписи типа, или кто-либо знает о хорошей ссылке на этом материале (единственная вещь, которая помогла, до сих пор были "Преобразователи монады Шаг за шагом", но это просто использует одну функцию состояния aux и один StateT)?

Заранее большое спасибо.

Править
Вот код компиляции, включающий предложения JFT и Edward:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- needed for: deriving (Functor,Monad)
{-# LANGUAGE MultiParamTypeClasses #-}      -- needed for: MonadState instance
{-# LANGUAGE FlexibleContexts #-}           -- needed for: (MonadState PassState m) => ...

module Pass1 where
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import Types

type Lines     = [Line]
type Addresses = [Address]
type LineNum   = Int
type Messages  = [Msg]
data Msg = Error String
         | Warning String

data PassState = PassState { passLineNum :: LineNum
                           , passLines :: Lines
                           , passAddresses :: Addresses
                           }

newtype Pass1 a = Pass1 { unPass1 :: WriterT Messages (State PassState) a
                        }
                        deriving (Functor,Monad)

instance MonadState PassState Pass1 where
        get   = Pass1 . lift $ get
        put s = Pass1 . lift $ put s



runPass1 :: PassState -> Pass1 a -> ((a, Messages), PassState)
runPass1 state = flip runState state .
                 runWriterT          .
                 unPass1


curLineNum :: (MonadState PassState m) => m LineNum
curLineNum = do
               state <- get
               return $ passLineNum state


nextLine :: (MonadState PassState m) => m (Maybe Line)
nextLine = do
             state <- get
             let c = passLineNum state
             let l = passLines state
             case l of
               x:xs -> do
                         put state { passLines = xs, passLineNum = (c+1) }
                         return $ Just x
               _ -> return Nothing



evalr :: Pass1 (Maybe Line,LineNum)
evalr = do
          l <- nextLine
          c <- curLineNum
          --tell $ Warning "hello"
          return (l,c)

Я объединился incLineNum и popLine в nextLine Я все еще должен заставить часть монады Устройства записи работать, но думать я знаю, куда пойти отсюда.Спасибо, ребята.

14
задан paul 18 January 2010 в 22:10
поделиться

2 ответа

Было много вопросов с вашим фрагментом кода. Я зафиксировал свой фрагмент, добавляя объяснение о том, что было сломано и добавлено несколько стилей, если вы заботитесь.

module Pass1_JFT where
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map

{- замена ваших типов импорта с помощью простых определений -}

--import Types
type Line       = String
type Address    = String
type LineNumber = Int

{- Не часть вашего вопроса, но мои 2 цента здесь ... Скажите, что вы хотите изменить коллекцию для своих штатов, если вы не Используйте псевдоним типа, которым вы должны охотиться, вы использовали его. Вместо этого изменить эти определения, если требуется -}

type Lines     = [Line]
type Addresses = [Address]
type Messages  = [Msg]


data Msg = Error String
         | Warning String

{- Что это в INT в StateT INT? Имя легче читать, причина о и изменить. Декларативный FTW Давайте вместо этого будем использовать LineNumber -}

--type Pass1 a = WriterT [Msg] (StateT Int (StateT [Line] (StateT [Address] Identity))) a

{- Давайте использовать «реальный» тип, поэтому могут быть получены экземпляры. Поскольку Pass1 не является монадской передачей I.e. Не определяется как pass1 m a, Нет смысла с использованием StateT для самой глубокой статует I.e. Статуше [Адрес] Identity Итак, давайте просто используем состояние [адрес] -}

newtype Pass1 a = Pass1 {
    unPass1 :: WriterT Messages (StateT LineNumber (StateT Lines (State Addresses))) a
                        }
                        deriving (Functor,Monad)

--runIdentity (runStateT (runStateT (runStateT (runWriterT msgs) 1) instrs) addrs)

{- Давайте очистить этот стек от внешней (ленду в декларации) до самой внутренней была личность в вашей первоначальной декларации. Обратите внимание, что runwritert не принимает исходное состояние ... Первый параметр для Runstatet (и запуска) не является начальным состоянием Но монад ... так что давайте перевернумся! -}

runPass1' :: Addresses -> Lines -> Messages -> Pass1 a ->  ((((a, Messages), LineNumber), Lines), Addresses)
runPass1' addrs instrs msgs = flip runState addrs   .
                              flip runStateT instrs .
                              flip runStateT 1      .
                              runWriterT            . -- then get process the WriterT (the second outermost)
                              unPass1                 -- let's peel the outside Pass1

{- Теперь, когда последняя функция не делает то, что вы хотите, поскольку вы хотите предоставить Первоначальный журнал, чтобы добавить с помощью Writert. Так как это монадский трансформер, мы сделаем здесь какой-то трюк -}

-- I keep the runStateT convention for the order of the arguments: Monad then state
runWriterT' :: (Monad m,Monoid w) => WriterT w m a -> w -> m (a,w)
runWriterT' writer log = do
    (result,log') <- runWriterT writer
    -- let's use the monoid generic append in case you change container...
    return (result,log `mappend` log')

runPass1 :: Addresses -> Lines -> Messages -> Pass1 a ->  ((((a, Messages), LineNumber), Lines), Addresses)
runPass1 addrs instrs msgs = flip runState addrs   .
                             flip runStateT instrs .
                             flip runStateT 1      .
                             flip runWriterT' msgs . -- then get process the WriterT (the second outermost)
                             unPass1                 -- let's peel the outside Pass1

{- Вы собираетесь вызвать Popline прямо из стека Pass1? Если это так, вам нужно «преподавать» Pass1 быть «монадциональными линиями» Чтобы сделать это, давайте выведем Pass1 (именно поэтому мы объявили это с Newtype!) -}

instance MonadState Lines Pass1 where
    -- we need to dig inside the stack and "lift" the proper get
    get   = Pass1 . lift . lift $ get
    put s = Pass1 . lift . lift $ put s

{- Лучше держать вещи общего, но теперь мы могли бы написать: PopLine :: Pass1 (может быть, линия) -}

popLine :: (MonadState Lines m) => m (Maybe Line)
popLine = do
        ls <- get
        case ls of
          x:xs -> do
                    put xs
                    return $ Just x
          []   -> return Nothing

{- Хорошо, теперь я получаю INT => LineNumber .... Мы могли бы сделать Pass1 и экземпляр монадситного линомера, но линенама не следует беспокоить, поэтому я бы справился с наклоном напрямую И предоставит бы пример монадреада для консультации при необходимости

check ":t incLineNum and :t curLineNum"

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

Там он является длительным откликом, но монад и монадский стек, как вы видите, сначала сложно. Я исправил код, но я рекомендую вам играть и осмотреть типы различных функций, чтобы понять, что происходит и сравнивать с вашим оригиналом. Вывод типа Haskell означает, что обычно типа аннотации являются лишние (если не удалить неоднозначность). В целом тип, который мы дадим к функции, меньше общего, что было сделано, так что лучше не вводить аннотата. Аннотация типа окончательно является хорошей техникой отладки;)

ура

P.S. Глава Real World Haskell на монадском трансформаторе отличная: http://book.realworldhaskell.org/ruad/monad-transformers.html

39
ответ дан 1 December 2019 в 06:15
поделиться

В целом вы обнаружите, что код накапливается намного более четким, используя одну статуту с большей составной структурой для всех нужных вам битов состояния. Одна хорошая причина в том, что когда вы придумываете кусок государства, вы забыли, вы всегда можете вырастить структуру одним полем, и вы можете использовать запись сахара, чтобы записать обновления одного поля или обратиться к чему-то вроде FCLABELS или Data-Accessor Пакеты для манипулирования государством.

data PassState = PassState { passLine :: Int, passLines :: [Line] }

popLine :: MonadState PassState m => m (Maybe Line).   
popLine = do
   state <- get
   case passLines state of
      x:xs -> do 
         put state { passLines = xs }
         return (Just x)
      _ -> return Nothing
12
ответ дан 1 December 2019 в 06:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: