2013-02-01 12 views
13

Dlaczego w Haskell istnieją dwie różne monady typu Writer? Intuicyjnie dla mnie czytanie "ścisłej monad pisarza" oznacza, że ​​<> jest ścisły, więc nie ma w nim nagromadzenia thunk. Jednak patrząc na kod źródłowy, okazuje się, że nie jest to przypadek:Jaki jest sens posiadania leniwej/ścisłej wersji Writera?

-- Lazy Writer 
instance (Monoid w, Monad m) => Monad (WriterT w m) where 
-- ... 
m >>= k = WriterT $ do 
    ~(a, w) <- runWriterT m 
    ~(b, w') <- runWriterT (k a) 
    return (b, w <> w') 

W ścisłym wersji wzory nie są niepodważalne, to znaczy ~ brakuje. Tak więc powyżej powyższe jest to, że m i k a nie są oceniane, ale przechowywane jako thunks. W wersji ścisłej są one oceniane w celu sprawdzenia, czy pasują do wzorców krotek, wynik jest podawany do <>. W obu przypadkach wartość >>= nie jest obliczana, dopóki coś faktycznie nie wymaga wartości wynikowej. Tak więc rozumiem, że zarówno leniwe, jak i ścisłe wersje robią to samo, z wyjątkiem tego, że mają thunk w innym miejscu w definicji >>=: lazy produkuje runWriterT thunks, strict produkuje <> thunks.

To pozostawia mnie z dwoma pytaniami:

  1. Czy powyższe prawo, czy mogę rozumieją ocenę tutaj?
  2. Czy mogę wykonać ścisłą <> bez pisania własnego opakowania i instancji?
+3

Na pytanie 1 odpowiedź brzmi: http://stackoverflow.com/questions/13186512/difference-between-haskells-lazy-and-strict-monads-or-transformers –

Odpowiedz

15

Pierwsza obserwacja jest prawidłowa, ale to rozróżnienie, między którym powstają tony, jest ważne.

Lazy i Strict nie dotyczą ścisłości w typie dziennika, ale zamiast ścisłości w parze.

Powstają, ponieważ para w Haskell ma dwa możliwe sposoby aktualizacji.

bimap f g (a,b) = (f a, g b) 

lub

bimap f g ~(a,b) = (f a, g b) 

Drugi jest taki sam jak

bimap f g p = (f (fst p), g (snd p)) 

Różnica między nimi polega na tym, że kiedy przechodzi się argumenty z bimap w pierwszym przypadku, para jest zmuszony natychmiast.

W tym ostatnim przypadku para nie jest natychmiast wymuszona, ale zamiast tego przekazuję ci jedną z dwóch niecałkowitych obliczeń.

Oznacza to, że

fmap f _|_ = _|_ 

w pierwszym przypadku, ale

fmap f _|_ = (_|_, _|_) 

w parze przypadku drugi leniwy!

Obie są poprawne pod różnymi interpretacjami pojęcia pary. Jeden jest na ciebie zmuszony, udając, że para jest parą w kategorycznym znaczeniu, że nie ma żadnego interesującego _|_ na swoim własnym. Z drugiej strony, interpretacja domeny jako nie ścisłej.tak, jak to możliwe, aby można było zakończyć jak najwięcej programów w wersji Lazy.

(,) e to idealnie dopuszczalne Writer, więc to charakteryzuje problem.

Powodem tego rozróżnienia jest to, że ma znaczenie dla zakończenia wielu egzotycznych programów, które przyjmują stały punkt przez monadę. Możesz odpowiadać na pytania dotyczące pewnych okrągłych programów obejmujących stan lub pisarz, o ile są one Lazy.

Uwaga, w żadnym wypadku nie jest to ścisłe w argumencie "log". Po tym, jak stracisz poczucie ścisłości, utracisz odpowiednią łączność i przestaniesz być technicznie Monad. =/

Ponieważ nie jest to monada, nie dostarczamy jej w wersji mtl!

Dzięki temu możemy zająć drugie pytanie:

Istnieją pewne obejścia chociaż. Możesz skonstruować fałszywego Writer na wierzchu State. Zasadniczo udawaj, że nie przekazano ci argumentów państwowych. i po prostu zamień się w stan tak, jakbyś był tell. Teraz możesz to zrobić ściśle, ponieważ nie dzieje się za plecami jako część każdego wiązania. State przechodzi właśnie przez stan niezmodyfikowany między akcjami.

shout :: Monoid s => s -> Strict.StateT s m() 
shout s' = do 
    s <- get 
    put $! s <> s' 

ta ma jednak oznaczać, że zmuszają całą swoją State monady, aby uzyskać moc i nie może produkować części Monoid leniwie ale masz coś, co jest funkcjonalnie bliżej czym jest ścisła programista byłoby oczekiwać. Co ciekawe, działa to nawet po prostu Semigroup, ponieważ jedyne użycie mempty jest skutecznie na początku, gdy runState.

+1

Obserwacja o leniwych parach: zwykła '(,) 'nie jest produktem w sensie kategorycznym ze względu na dodatkowe dno. Jeśli Haskell nie miał "seq", można by zdefiniować moduł z typem "data Pair a b = Pair a b", który nie eksponował konstruktora, ale ujawnił funkcje, aby utworzyć te pary i uzyskać pierwszą i drugą wartość. Modularność sprawiłaby, że zachowałaby się w sposób obserwacyjny jako prawdziwy produkt (IMO, który jest wystarczająco dobry). Lub użyj kodowania Kościoła. Mamy 'seq' chociaż, a ścisłe języki nie mogą mieć produktów, więc nie mamy szczęścia, jeśli chodzi o matematyczną dokładność. –

+0

Czy możesz podać przykład miejsca, w którym wersja Strict może być przydatna? Nie rozumiem, jak to może być lepsze od leniwego. –