Question

Je cherche à créer une pile de transformateurs monade et ai du mal à obtenir les signatures de type correct pour mes fonctions. (Je suis encore assez nouveau pour Haskell)

La pile combine plusieurs transformateurs de StateT depuis que j'ai plusieurs états dont j'ai besoin de garder une trace de (dont deux pourraient être uplées, mais je vais y revenir dans une seconde) et un WriterT pour l'enregistrement.

Voici ce que j'ai à ce jour:

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

Je voudrais que le popLine au mess avec l'état [Line] et les fonctions de xLineNum d'affecter l'état de Int. evalr est le calcul qui sera transmis à runPass1.

Chaque fois que je charge le code que je lance dans des erreurs qui sont généralement de la variété suivante:

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 }

Aucune des signatures semblent corrects, mais popeline est la première fonction il est le seul qui provoque immédiatement une erreur.

J'essaie d'ajouter ce qu'il suggère dans la signature de type (par exemple: popLine :: (MonadState [Line] m) => ... mais il des erreurs comme ceci:

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)

Il me semble toujours d'obtenir ce message chaque fois que j'essaie de faire quelque chose qui est pas une variable de type. Il semble que (MonadState s m) ok et erreur sur quelque chose d'autre, mais quand je l'essayer avec un [a] au lieu d'erreurs s de semblable à ce qui précède. (Dans un premier temps le dans un état unique [ligne] et Int ont été uplées, mais je recevais cette erreur, donc je pensais que je vais essayer de les mettre dans des états séparés).

GHC 6.10.4, Kubuntu

Alors, quelqu'un peut me dire ce qui se passe et donner une explication / me montrer les signatures de type droit, ou personne ne sait d'une bonne référence sur ce genre de choses (la seule chose qui a aidé à ce jour était « Monad Transformateurs étape par étape », mais qui utilise une seule fonction publique et aux StateT un)?

Beaucoup

Merci à l'avance.

Modifier Voici le code de compilation intégrant les suggestions de son JFT et 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)

I combiné incLineNum et popLine en nextLine je dois encore obtenir la partie Writer monade au travail, mais pense que je sais où aller d'ici. Merci, les gars.

Était-ce utile?

La solution

Il y avait beaucoup de problèmes avec l'extrait de code. Je fixe votre extrait en ajoutant des explications à ce qui a été cassé et a ajouté quelques conseils de style si vous aimez.

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

{- remplacer vos types d'importation avec de simples définitions -}

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

{-     Ne fait pas partie de votre question, mais mes 2 cents ici ...     Dites que vous voulez changer la collection pour vos états si vous ne     utiliser un alias de type que vous devrez chasser everwhere vous l'avez utilisé. Au lieu de cela seulement     modifier ces définitions si nécessaire -}

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


data Msg = Error String
         | Warning String

{-     Qu'est-ce que Int dans StateT Int? Nommez plus facile à lire, à propos de la raison     et de changer. Déclarative FTW Utilisons LineNumber à la place -}

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

{-     Utilisons un type « réel » si les instances peuvent être dérivées.     Depuis Pass1 constitue pas un transfert de monade à-dire non défini comme un Pass1 m,     aucun point à l'aide StateT pour le plus profond StateT à savoir StateT [Adresse] Identité     donc nous allons simplement utiliser un Etat [Adresse] -}

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)

{-     Nous allons peler que la pile à partir de la plus externe (lefmost dans la déclaration)     jusqu'à la plus interne était identité dans votre déclaration d'origine.     Notez que runWriterT ne prend pas un état de départ ...     Le premier paramètre pour runStateT (et runState) ne soit pas l'état initial     mais la monade ... alors laissez Renversons! -}

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

{-     maintenant que la dernière fonction ne fait pas ce que vous voulez puisque vous voulez fournir     un journal initial à ajouter à la WriterT.     Comme il est un transformateur de monade nous ferons une astuce ici -}

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

{-     Avez-vous l'intention d'appeler directement popeline à partir d'une pile Pass1?     Si oui, vous devez « enseigner » Pass1 être un « Lignes MonadState »     Pour ce faire, nous allons Pass1 dériver (c'est la raison pour laquelle nous avons déclaré avec 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

{-    Mieux vaut garder chose générique, mais nous pourrions maintenant avoir écrit:    popeline :: Pass1 (Peut-être la ligne) -}

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 maintenant je Int => LineNumber ....     nous pourrions faire Pass1 et instance de MonadState LineNumber mais LineNumber     ne devrait pas être sali avec de sorte qu'au lieu je voudrais coder directement le Incline     et fournirait une instance MonadReader pour consulation si nécessaire

check ":t incLineNum and :t curLineNum"

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

Il est une réponse de longue haleine, mais monade et pile monade que vous voyez sont difficile au début. Je fixe le code mais je vous encourage à jouer et inspecter les types des différentes fonctions de comprendre ce qui se passe et à comparer à l'original. L'inférence de type de Haskell signifie que généralement de type annotations sont superflues (à moins d'éliminer l'ambiguïté). En général, le type que nous donnerions à la fonction est moins générique qui était est infère il est donc préférable de ne pas taper annoter. annotation de type est définitivement une bonne technique de débogage bien;)

Vive

P.S. chapitre Real World Haskell sur Monad Transformer est excellente: http://book.realworldhaskell.org/read/monad-transformers.html

Autres conseils

En général, vous trouverez que le code serpente beaucoup plus claire à l'aide d'un StateT avec une structure composite plus grande pour tous les bits d'état que vous avez besoin. Une bonne raison est que lorsque vous venez avec un morceau de l'état que vous avez oublié, vous pouvez toujours développer la structure par un champ, et vous pouvez utiliser le sucre d'enregistrement pour écrire des mises à jour sur le terrain unique ou se tourner vers quelque chose comme les fclabels ou données accesseur paquets pour manipuler l'état.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top