Я пытаюсь создать стопку преобразователей монады и испытываю затруднения при получении корректных подписей типа для моих функций. (Я все еще довольно плохо знаком с 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
Я все еще должен заставить часть монады Устройства записи работать, но думать я знаю, куда пойти отсюда.Спасибо, ребята.
Было много вопросов с вашим фрагментом кода. Я зафиксировал свой фрагмент, добавляя объяснение о том, что было сломано и добавлено несколько стилей, если вы заботитесь.
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
В целом вы обнаружите, что код накапливается намного более четким, используя одну статуту с большей составной структурой для всех нужных вам битов состояния. Одна хорошая причина в том, что когда вы придумываете кусок государства, вы забыли, вы всегда можете вырастить структуру одним полем, и вы можете использовать запись сахара, чтобы записать обновления одного поля или обратиться к чему-то вроде 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