سؤال

أحاول إنشاء كومة من محولات Monad وأواجه مشكلة في الحصول على توقيعات النوع الصحيحة لوظائفي. (ما زلت جديدًا على هاسكل)

يجمع المكدس بين محولات Statet متعددة نظرًا لأن لديّ حالات متعددة أحتاج إلى تتبعها (يمكن أن يكون اثنان منها مضطربًا ، لكنني سأصل إلى ذلك في ثانية) وكتاب لتسجيل الدخول.

هذا ما لدي حتى الآن:

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] بدلاً من s أخطاء مماثلة لما سبق. (في البداية كان [الخط] و int في حالة واحدة ، لكنني كنت أتلقى هذا الخطأ لذلك اعتقدت أنني سأحاول وضعها في حالات منفصلة).

GHC 6.10.4 ، Kubuntu

لذا ، هل يمكن لأي شخص أن يخبرني بما يجري وإعطاء شرح / أظهر لي توقيعات النوع الصحيحة ، أو هل يعرف أي شخص مرجعًا جيدًا على هذه الأشياء (الشيء الوحيد الذي ساعد حتى الآن هو "Monad Transformers خطوة بخطوة" ، لكن هذا يستخدم وظيفة حالة 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 ما زلت بحاجة إلى جعل الكاتب موناد جزءًا من العمل ، لكنني أعتقد أنني أعرف من أين أذهب من هنا. شكرا يا رفاق.

هل كانت مفيدة؟

المحلول

كان هناك العديد من المشكلات مع مقتطف الكود الخاص بك. لقد أصلحت المقتطف الخاص بك إضافة شرح لما تم كسره وأضفت بعض النصائح الأناقة إذا كنت تهتم.

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 التصريحي لنستخدم عدد الكتان بدلاً من ذلك -}

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

{- دعنا نستخدم نوعًا "حقيقيًا" بحيث يمكن اشتقاق الحالات. نظرًا لأن Pass1 ليس نقل موناد ، أي لم يتم تعريفه على أنه Pass1 Ma ، لا توجد فائدة من استخدام Statet لأعمق هوية STATET IE [العنوان] لذلك دعونا فقط نستخدم حالة [العنوان] -}

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 (و RunState) ليست الحالة الأولية ولكن الموناد ... لذلك دعونا نقلب! -}

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 لتكون "خطوط Monadstate" للقيام بذلك ، دعنا نشتق 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 => عدد الكتان .... يمكن أن نجعل Pass1 ومثيل من الكتان monadstate ولكن لا ينبغي أن يفسد عدد الكتان مع ذلك بدلاً من ذلك ، سأقوم بترميز المنحدر مباشرةً وسأوفر مثيلًا monadreader للقنصلية إذا لزم الأمر

check ":t incLineNum and :t curLineNum"

-}

incLineNum = Pass1 . lift $ modify (+1)

curLineNum = Pass1 $ lift get

evalr = do l <- popLine
           incLineNum
           return l

هناك استجابة طويلة المذهلة ولكن Monad و Monad Stack كما تراه يمثل تحديًا في البداية. لقد أصلحت الرمز ولكني أشجعك على لعب وتفقد أنواع الوظائف المختلفة لفهم ما يجري والمقارنة مع الأصل. إن الاستدلال على نوع Haskell يعني أن التعليقات التوضيحية عادة ما تكون غير ضرورية (ما لم يتم إزالة الغموض). بشكل عام ، يكون النوع الذي نقدمه للوظيفة أقل عامًا وهو الاستنتاج ، لذا من الأفضل عدم كتابة التعليقات التوضيحية. نوع التعليق التوضيحي هو بالتأكيد تقنية تصحيح جيد على الرغم من ؛)

هتافات

PS real World Haskell Chapter on Monad Transformer ممتاز:http://book.realworldhaskell.org/read/monad-transformers.html

نصائح أخرى

بشكل عام ، ستجد أن الكود ينتهي أكثر وضوحًا باستخدام Statet واحد بهيكل مركب أكبر لجميع أجزاء الحالة التي تحتاجها. أحد الأسباب الجيدة هو أنه عندما تتوصل إلى قطعة من الحالة ، نسيت أنه يمكنك دائمًا زراعة الهيكل بموجب حقل واحد ، ويمكنك استخدام السكر السجل لكتابة تحديثات حقل مفردة أو تتحول إلى شيء مثل FCLABELS أو ACCOSSER حزم لمعالجة الدولة.

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top