Haskell'de Control.Monad.Writer ile nasıl oynanır?


97

İşlevsel programlama konusunda yeniyim ve kısa süre önce Learn You a Haskell'de öğreniyorum , ancak bu bölümden geçerken aşağıdaki programa takılıp kaldım:

import Control.Monad.Writer  

logNumber :: Int -> Writer [String] Int  
logNumber x = Writer (x, ["Got number: " ++ show x])  

multWithLog :: Writer [String] Int  
multWithLog = do  
    a <- logNumber 3  
    b <- logNumber 5  
    return (a*b)

Bu satırları bir .hs dosyasına kaydettim ancak şikayet eden ghci'me aktaramadım:

more1.hs:4:15:
    Not in scope: data constructor `Writer'
    Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.

Türü ": info" komutuyla inceledim:

Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
               -- Defined in `Control.Monad.Trans.Writer.Lazy'

Benim bakış açıma göre, bunun "newtype Writer wa ..." gibi bir şey olması gerekiyordu, bu yüzden veri oluşturucuyu nasıl besleyeceğim ve bir Yazar edinebileceğim konusunda kafam karıştı.

Sanırım sürümle ilgili bir sorun olabilir ve benim ghci sürümüm 7.4.1


2
Alıştırmalar için beyanı içe aktarmamanızı ve dosyaya kendi başınıza yazmanızı tavsiye ederim.
sdcvvc

5
Yazarla bir süre önce konuştum ve kitabın çevrimiçi versiyonunun güncel olmadığını doğruladı. PDF'de daha güncel bir sürüm var: [burada ]
Electric Coffee

@Elektrik, aynı sorum var, lütfen bir bağlantı verebilir misiniz? Yukarıdaki bağlantınız koptu.
Bulat M.

Yanıtlar:


127

Paket Control.Monad.Writer, veri yapıcısını dışa aktarmaz Writer. Sanırım bu LYAH yazıldığında farklıydı.

Ghci'de MonadWriter tip sınıfını kullanma

Bunun yerine, writerişlevi kullanarak yazarlar yaratırsınız . Örneğin, bir ghci oturumunda yapabilirim

ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])

Şimdi logNumberyazarları yaratan bir işlevdir. Türünü sorabilirim:

ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a

Bu da bana, çıkarılan türün belirli bir yazıcıyı döndüren bir işlev olmadığını , daha çok MonadWritertür sınıfını uygulayan herhangi bir şey olduğunu söylüyor . Şimdi onu kullanabilirim:

ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
    :: Writer [String] Int

(Giriş aslında tümü tek bir satıra girildi). Burada multWithLogolmanın türünü belirledim Writer [String] Int. Şimdi çalıştırabilirim:

ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])

Ve tüm ara işlemleri kaydettiğimizi görüyorsunuz.

Kod neden böyle yazılıyor?

Neden MonadWritertür sınıfını oluşturmakla uğraşasınız ki ? Nedeni monad transformatörlerle yapmaktır. Doğru bir şekilde fark ettiğiniz gibi, uygulamanın en basit yolu Writerbir çiftin üzerine yeni bir tür sarmalayıcıdır:

newtype Writer w a = Writer { runWriter :: (a,w) }

Bunun için bir monad örneği bildirebilir ve ardından işlevi yazabilirsiniz

tell :: Monoid w => w -> Writer w ()

basitçe girişini kaydeder. Şimdi, günlüğe kaydetme yeteneklerine sahip ama aynı zamanda başka bir şey yapan bir monad istediğinizi varsayalım - bir ortamdan da okuyabileceğini söyleyin. Bunu şu şekilde uygularsın

type RW r w a = ReaderT r (Writer w a)

Şimdi, yazıcı ReaderTmonad transformatörün içinde olduğu için , çıktıyı günlüğe kaydetmek istiyorsanız kullanamazsınız tell w(çünkü bu yalnızca sarmalanmamış yazarlar ile çalışır), ancak işlevi aracılığıyla lift $ tell w"kaldıran" , iç yazar monad. İki katmanlı transformatör istiyorsanız (ayrıca hata işleme eklemek istediğinizi söyleyin), o zaman kullanmanız gerekir . Bu hızla hantal hale gelir.tellReaderTlift $ lift $ tell w

Bunun yerine, bir tür sınıfı tanımlayarak, bir yazarın etrafındaki herhangi bir monad dönüştürücü sarmalayıcısını bir yazarın kendisi haline getirebiliriz. Örneğin,

instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)

yani wbir monoidse ve ma ise MonadWriter w, o ReaderT r mzaman da a'dır MonadWriter w. Bu, tellişlevi doğrudan dönüştürülmüş monad üzerinde, onu monad transformatör aracılığıyla açıkça kaldırmak zorunda kalmadan kullanabileceğimiz anlamına gelir .


31
"Sanırım bu, LYAH yazıldığında farklıydı." Sağ. Bu mtl, LYAH ve RWH yazıldıktan kısa bir süre sonra ana sürüm 1. * 'den 2. *' ye geçerken değişti. Yeni başlayanlar arasında çok fazla kafa karışıklığına yol açan ve yol açan son derece talihsiz zamanlama.
Daniel Fischer

2
Şu anda GHC 7.8.3 sürümünü kullanıyorum ve içe aktarmam gerekiyordu Control.Monad.Trans.Writer. Ayrıca türü logNumberolan logNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m abenim için.
kmikael

@kmikael Büyük olasılıkla mtlkütüphane kurulu değildir (bu muhtemelen Haskell Platformu yerine minGHC gibi temel bir GHC kurulumuna sahip olduğunuz anlamına gelir). Bir komut istemi vadede itibaren cabal updateve cabal install mtldaha sonra tekrar deneyin.
Chris Taylor

Chris, Haskell Platformunu kullanıyordum ve mtl kuruldu, yine de tekrar kurdum ve şimdi cevabınızdaki gibi çalışıyor gibi görünüyor. Neyin yanlış olduğunu bilmiyorum. Teşekkürler.
kmikael

Bunun yerine kitabın basılı kopyası doğrudur. İkincisi writeryerine kullanılan Writerdeğer ctorunun modül tarafından dışa aktarılmadığını, birincisi ise olduğunu açıklayan bir paragraf içerir ve ctor ile yaratacağınız aynı değeri oluşturmak için kullanılabilir, ancak desen eşleşmesine izin verilmez.
Enlico

8

"Writer" kurucusu yerine "writer" adlı bir işlev kullanıma sunulur. Değişiklik:

logNumber x = Writer (x, ["Got number: " ++ show x])

to:

logNumber x = writer (x, ["Got number: " ++ show x])


6
Bu, mevcut cevaplara ne ekler?
dfeuer

1

Ben LYAH çalışırken benzer bir mesajım var "Daha birkaç monad'ların için" online Haskell düzenleyicisi kullanarak repl.it

İçe aktarımı şuradan değiştirdim:

import Control.Monad.Writer

to:

import qualified Control.Monad.Trans.Writer.Lazy as W

Yani kodum artık şöyle görünüyor ( Kwang'ın Haskell Blogundan ilham alarak ):

import Data.Monoid
import qualified Control.Monad.Trans.Writer.Lazy as W


output :: String -> W.Writer [String] ()
output x = W.tell [x]


gcd' :: Int -> Int -> W.Writer [String] Int  
gcd' a b  
    | b == 0 = do  
        output ("Finished with " ++ show a)
        return a  
    | otherwise = do  
        output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b))
        gcd' b (a `mod` b)

main :: IO()
main = mapM_ putStrLn $ snd $ W.runWriter (gcd' 8 3) 

Kod şu anda burada çalıştırılabilir

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.