Frage

Ich versuche, einen Stapel von Monadentransformatoren zu erstellen, und habe Probleme, die richtigen Typsignaturen für meine Funktionen zu erhalten.(Ich bin noch ziemlich neu bei Haskell)

Der Stapel kombiniert mehrere StateT-Transformatoren, da ich mehrere Zustände habe, die ich im Auge behalten muss (zwei davon könnten getupft werden, aber dazu komme ich gleich) und einen WriterT für die Protokollierung.

Folgendes habe ich bisher:

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

Ich möchte das popLine sich damit anlegen [Line] Staat und die xLineNum Funktionen, die die beeinflussen Int Zustand. evalr ist die Berechnung, an die übergeben wird runPass1.

Immer wenn ich den Code lade, treten Fehler auf, die im Allgemeinen der folgenden Art angehören:

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 }

Keine der Signaturen scheint korrekt zu sein, aber popLine ist die erste Funktion und somit die einzige, die sofort einen Fehler verursacht.

Ich versuche, das, was es in der Typsignatur vorschlägt, hinzuzufügen (z. B.: popLine :: (MonadState [Line] m) => ... aber dann kommt es zu einem Fehler wie folgt:

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)

Ich erhalte immer diese Meldung, wenn ich versuche, etwas zu tun, das keine Typvariable ist.Es scheint zu gefallen (MonadState s m) ok und Fehler bei etwas anderem, aber wenn ich es mit einem versuche [a] anstatt s Es treten ähnliche Fehler wie oben auf.(Ursprünglich waren [Line] und Int in einem einzelnen Status getupelt, aber ich erhielt diese Fehlermeldung und dachte, ich würde versuchen, sie in separate Zustände zu versetzen.)

GHC 6.10.4, Kubuntu

Kann mir also jemand sagen, was los ist, und eine Erklärung geben/mir die richtigen Typsignaturen zeigen, oder kennt jemand eine gute Referenz zu diesem Thema (das Einzige, was bisher geholfen hat, war „Monad Transformers Step by Step“) , aber das verwendet nur eine Aux-State-Funktion und ein StateT)?

Vielen Dank im Voraus.

Bearbeiten
Hier ist der Kompilierungscode, der die Vorschläge von JFT und Edward berücksichtigt:

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

Ich habe kombiniert incLineNum Und popLine hinein nextLine Ich muss den Writer-Monadenteil noch zum Laufen bringen, denke aber, dass ich weiß, wohin ich von hier aus gehen soll.Danke Leute.

War es hilfreich?

Lösung

Es gab viele Probleme mit Ihrem Code-Snippet.Ich habe Ihr Snippet korrigiert, eine Erklärung hinzugefügt, was kaputt war, und ein paar Stiltipps hinzugefügt, falls es Sie interessiert.

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

{- Ersetzen Sie Ihre Importtypen durch einfache Definitionen -}

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

{- Nicht Teil Ihrer Frage, sondern meine 2 Cent hier ...Angenommen, Sie möchten die Sammlung für Ihre Bundesstaaten ändern, wenn Sie dies nicht tun Verwenden Sie einen Typ-Alias, den Sie überall dort suchen müssen, wo Sie ihn verwendet haben.Stattdessen einfach Ändern Sie diese Definitionen bei Bedarf -}

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


data Msg = Error String
         | Warning String

{- Was ist das Int in StateT Int?Nennen Sie es leichter zu lesen, argumentieren Sie darüber und zu verändern.Deklaratives FTW verwenden wir stattdessen LineNumber -}

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

{- Lassen Sie uns einen "echten" Typ verwenden, damit Instanzen abgeleitet werden können.Da Pass1 kein Monadentransfer ist, d.h.nicht definiert als Pass1 m a, Es macht keinen Sinn, StateT für den tiefsten StateT zu verwenden, d.h.StateT [Adresse] Identität also lassen Sie uns einfach einen Staat [Adresse] verwenden -}

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)

{- Lassen Sie uns diesen Stapel aus dem äußersten (lefmost in der Deklaration) abziehen bis ins Innerste war Identität in deiner ursprünglichen Erklärung.Beachten Sie, dass runWriterT KEINEN Startstatus annimmt ...Der erste Parameter für runStateT (und runState) ist nicht der Anfangszustand Aber die Monade...Also lasst uns umdrehen!-}

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

{- Diese letzte Funktion tut NICHT das, was Sie wollen, da Sie ein anfängliches Protokoll, das mit dem WriterT angefügt werden soll.Da es sich um einen Monaden-Transformator handelt, machen wir hier einen Trick -}

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

{- Beabsichtigen Sie, popLine direkt von einem Pass1-Stack aus aufzurufen?Wenn ja, müssen Sie Pass1 "lehren", ein "MonadState Lines" zu sein Dazu leiten wir Pass1 ab (deshalb haben wir ihn mit newtype deklariert!) -}

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

{- Es ist besser, die Sache generisch zu halten, aber wir hätten jetzt schreiben können:popLine ::Pass1 (vielleicht Linie) -}

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, jetzt bekomme ich die Int => LineNumber....Wir könnten Pass1 und Instanz von MonadState LineNumber aber LineNumber machen sollte nicht durcheinander gebracht werden, also würde ich stattdessen die Neigung direkt codieren und würde bei Bedarf eine MonadReader-Instanz für die Konsultation bereitstellen

check ":t incLineNum and :t curLineNum"

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

Das ist eine langwierige Antwort, aber wie Sie sehen, sind Monaden und Monadenstapel zunächst eine Herausforderung.Ich habe den Code korrigiert, aber ich empfehle Ihnen, die Typen der verschiedenen Funktionen auszuprobieren und zu untersuchen, um zu verstehen, was vor sich geht, und um sie mit Ihrem Original zu vergleichen.Haskells Typinferenz bedeutet, dass Typanmerkungen normalerweise überflüssig sind (es sei denn, um Mehrdeutigkeiten zu beseitigen).Im Allgemeinen ist der Typ, den wir der Funktion zuweisen würden, weniger generisch als abgeleitet, daher ist es besser, keine Anmerkungen einzugeben.Typanmerkungen sind jedoch definitiv eine gute Debugging-Technik ;)

Prost

P.S.Das Real World Haskell-Kapitel über Monad Transformer ist ausgezeichnet:http://book.realworldhaskell.org/read/monad-transformers.html

Andere Tipps

Im Allgemeinen werden Sie feststellen, dass der Code mit einem Statet mit einer größeren Verbundstruktur für alle Zustandsbits, die Sie benötigen, viel klarer werden. Ein guter Grund ist, dass Sie, wenn Sie ein Stück Zustand erstellen, Sie vergessen haben, die Struktur immer um ein Feld zu erweitern, und Sie können den Datensatz Zucker verwenden, um Einzelfeldaktualisierungen zu schreiben oder sich an so etwas wie die Fclabels oder Data-Accessor zu wenden Pakete zur Manipulation des Zustands.

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top