2012-09-02 18 views
5
class Monad m => MonadState s m | m -> s where 
    -- | Return the state from the internals of the monad. 
    get :: m s 
    get = state (\s -> (s, s)) 

    -- | Replace the state inside the monad. 
    put :: s -> m() 
    put s = state (\_ -> ((), s)) 

    -- | Embed a simple state action into the monad. 
    state :: (s -> (a, s)) -> m a 
    state f = do 
     s <- get 
     let ~(a, s') = f s 
     put s' 
     return a 

instance MonadState s m => MonadState s (MaybeT m) where... 

Dlaczego instancja MonadState potrzebuje stanu i monady, dlaczego nie utworzyć pojedynczego parametru State class?Dlaczego warto używać MultiParamTypeClasses w MonadState?

+4

Nie jestem pewien, czy rozumiem alternatywę, którą sugerujesz. Jak napisalibyśmy typ 'state :: (s -> (a, s)) -> m a' bez obu' m' i 's'? – Owen

+0

powiedzmy, że zaczniemy od 'class MonadState s, gdzie ...' a my po prostu robimy 'get :: s' i' put :: s ->() 'bez wprowadzania s do monady? Czy można osiągnąć prostszą implementację stanu, w której nie musimy się martwić, czy jest to stan Może, czy stan, czy stan IO? –

Odpowiedz

6

Pozwól mi spróbować odpowiedzieć na pytanie Gert w komentarzach, ponieważ jest to całkiem inne pytanie.

Pytanie brzmi, dlaczego nie możemy po prostu napisać

class State s where 
    get :: s 
    put :: s ->() 

Cóż, moglibyśmy napisać to. Ale teraz pytanie brzmi: co możemy z tym zrobić? A najtrudniejsze jest to, że jeśli mamy jakiś kod z put x, a później get, w jaki sposób możemy połączyć get z put, aby ta sama wartość została zwrócona, jak ta wprowadzona? Ten problem dotyczy tylko typów () i s. Możesz spróbować wdrożyć go na różne sposoby, ale to nie zadziała. Nie ma sposobu, aby przenieść dane z put do get (może ktoś może to wyjaśnić lepiej, ale najlepszym sposobem na zrozumienie jest próba napisania tego).

monady niekoniecznie jest jedynym sposobem, aby operacje skorelowane, ale jest to sposób, ponieważ ma operatora >> aby połączyć dwa oświadczenia ze sobą:

(>>) :: m a -> m b -> m b 

więc możemy napisać

(put x) >> get 

Edycja: Poniżej przedstawiono przykład użycia the StateT instance defined in the package

foo :: StateT Int IO() 
foo = do 
    put 3 
    x <- get 
    lift $ print x 

main = evalStateT foo 0 
+0

czy możesz dodać przykład IO do swojej odpowiedzi? Podobnie jak '(wstaw x) >> get >> = (\ x -> print x)' lub coś takiego? –

+1

@GertCuykens Nie mogę myśleć jak to zrobić z 'IO'; 'IO' nie ma miejsca na umieszczenie stanu takiego jak' State'. Ale mogę dodać przykład używając 'StateT' wokół' IO'. – Owen

+0

@Owen - możesz zrobić przykład używając '' IO' z 'IORef'. –

4

Potrzebujesz jakiegoś sposobu powiązania typu stanu z typem monady. MultiParamTypeClasses z FunctionalDependencies jest jednym ze sposobów. Możesz jednak zrobić to również za pomocą TypeFamilies. Jest to podejście przyjęte przez monads-tf package.

+1

Uwaga: 'monady-tf' jest w zasadzie wycofane. Pierwotnie Ross podzielił 'mtl' na' transformatory', który był Haskell 98 i dwa pakiety 'monads-tf' oraz' monads-fd', dzięki czemu ludzie mogli wybrać, który styl preferowali. Jednak to było straszne, ponieważ podzieliło społeczność na 3 sposoby, ponieważ 'mtl',' monads-tf' i 'monads-fd' wszystkie używały tych samych nazw modułów! Poprawka polegała na wycofaniu starego 'mtl' i przekształceniu' monads-fd' w 'mtl' i pozwól' monads-tf' umrzeć. Nie sprzeciwiłbym się pakietowi z klasami z 'monads-tf', który byłby wyłączony przy użyciu różnych nazw modułów, ale w jego obecnym kształcie jest wręcz szkodliwy. –

Powiązane problemy