Pregunta

Estoy intentando crear una pila de transformadores monad y estoy teniendo problemas para conseguir las firmas tipo correcto para mis funciones. (Todavía estoy bastante nuevo en Haskell)

La pila combina múltiples transformadores Statet ya que tengo varios estados que necesito para realizar un seguimiento de los (dos de los cuales podrían ser tupled, pero voy a llegar a eso en un segundo) y una WriterT para el registro.

Esto es lo que tengo hasta ahora:

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

Me gustaría que el popLine meterse con el estado y las funciones [Line] xLineNum afectar al estado Int. evalr es el cálculo que se pasará a runPass1.

cuando cargo el código que se producen errores de que son generalmente de la siguiente variedad:

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 }

Ninguna de las firmas parece ser correcta, pero popline es la primera función de lo que es el único que provoca inmediatamente un error.

Trate de añadir lo que se sugiere en la firma tipo (por ejemplo: popLine :: (MonadState [Line] m) => ... pero luego él los errores de esta manera:

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)

Siempre parecen llegar este mensaje cada vez que intento hacer algo que no es una variable de tipo. Parece que como (MonadState s m) bien y error en otra cosa, pero cuando lo intento con un [a] en lugar de errores s Es similar a la anterior. (Inicialmente, el [Línea] y se Int tupled en un solo estado, pero que estaba recibiendo este error, así que pensé que iba a tratar de ponerlos en estados separados).

GHC 6.10.4, Kubuntu

Así que, ¿alguien puede decirme lo que está pasando y dar una explicación / muéstrame las firmas de tipos adecuados, o ¿alguien sabe de una buena referencia en esta materia (la única cosa que ha ayudado hasta ahora era "Monad transformadores reductores a paso", pero eso sólo utiliza una función de estado auxiliar y uno Statet)?

Muchas gracias de antemano.

Editar
Aquí está el código de compilación incorporando las sugerencias de JFT y Edward de:

{-# 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)

He combinado incLineNum y popLine en nextLine Todavía necesito para obtener la porción mónada Writer para trabajar, pero creo que sé a dónde ir desde aquí. Gracias, chicos.

¿Fue útil?

Solución

Hubo muchos problemas con el fragmento de código. He arreglado el fragmento de la adición de explicación de lo que estaba roto y añadido algunos consejos de estilo, si usted se preocupa.

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

{- la sustitución de los tipos de importación con las definiciones simples -}

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

{-     No es parte de su pregunta, pero mis 2 centavos aquí ...     Digamos que desea cambia la colección para sus estados si no lo hace     utilizar un alias de tipo tendrá que cazar a todas partes que lo utilizó. En su lugar sólo     cambiar estas definiciones, si es necesario -}

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


data Msg = Error String
         | Warning String

{-     Lo que es que en Statet Int Int? Nombrar más fácil de leer, razonar acerca     y para cambiar. FTW declarativa vamos a utilizar en lugar LineNumber -}

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

{-     Vamos a utilizar un tipo "real" para casos se pueden derivar.     Desde Pass1 no es una transferencia mónada es decir, no definido como Pass1 m a,     No tiene sentido utilizar Statet para la identidad más profunda Statet decir Statet [Dirección]     así que vamos a utilizar un Estado [Dirección] -}

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)

{-     Vamos a pelar esa pila desde el exterior (lefmost en la declaración)     hasta la más interna fue de identidad en su primera declaración.     Tenga en cuenta que runWriterT hace falta ser un estado inicial ...     El primer parámetro para runStateT (y runState) no es el estado inicial     pero la mónada ... así que vamos a voltear! -}

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

{-     ahora que la última función no hace lo que quiere, ya que desea ofrecer     un registro inicial para anexar a la WriterT.     Dado que es un transformador mónada que vamos a hacer algún truco aquí -}

-- 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

{-     ¿Tiene la intención de llamar directamente desde un popline Pass1 apilar?     Si lo que necesita para "enseñar" Pass1 a ser un "MonadState Lines"     Para ello vamos a derivar Pass1 (por eso declaramos con 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

{-    Mejor mantener a lo genérico, pero que ahora podríamos haber escrito:    popline :: Pass1 (Tal vez Line) -}

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

{-     Ok, ahora tengo la Int => LineNumber ....     podríamos hacer Pass1 y la instancia de MonadState LineNumber pero LineNumber     no debe ser ensuciado con lo que en lugar me gustaría codificar directamente la pendiente     y proporcionaría una instancia MonadReader para el consulation si es necesario

check ":t incLineNum and :t curLineNum"

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

No se trata de una respuesta de largo aliento, pero mónada y la mónada pila como mejor le está desafiando al principio. He arreglado el código, pero os animo a jugar e inspeccionar los tipos de las diversas funciones para entender lo que está pasando y va a comparar con el original. inferencia de tipos de Haskell significa que por lo general de tipo anotaciones son superfluas (a menos que para eliminar la ambigüedad). En general, el tipo que le daría a la función es menos genérico que era es inferir por lo que es mejor no escribir anotaciones. Tipo de anotación es definitivamente una buena técnica de depuración aunque;)

Saludos

P.S. Real capítulo Mundial Haskell en Mónada transformador es excelente: http://book.realworldhaskell.org/read/monad-transformers.html

Otros consejos

En general, usted encontrará que el código termina mucho más clara utilizando uno Statet con una estructura compuesta más grande de todos los bits de estado que usted necesita. Una buena razón es que cuando usted viene con una hoja de estado se le olvidó que siempre puede crecer la estructura de un campo, y se puede usar el azúcar registro para escribir actualizaciones de campo individuales o dar vuelta a algo así como las fclabels o datos de acceso paquetes para manipular estado.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top