web-gelistirme-sc.com

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

İşlevsel programlama konusunda yeni ve son zamanlarda Learn You a Haskell 'ta yeni öğrendim, ancak bu bölüm ' ü okuduğumda, aşağıdaki programla şaşırdı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 kaydetmiştim ancak şikayet ettiğim ghci'ime aktarmadı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" komutu ile 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 w a ..." .__ gibi bir şey olması gerekiyordu. Bu yüzden veri kurucuyu nasıl besleyeceğimi ve bir Writer alacağımı kafam karıştı.

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

81
Javran

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

Ghci'de MonadWriter yazı tipini kullanma

Bunun yerine, writer işlevini 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 logNumber, yazarları yaratan bir fonksiyondur. Türü için sorabilirim:

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

Bu da bana, çıkarılan türün bir particular writer döndüren bir işlev olmadığını, aksine MonadWriter type sınıfını uygulayan herhangi bir şey olduğunu söyler. Şimdi kullanabilirim:

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

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

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

Görüyorsunuz, tüm ara işlemleri kaydediyoruz.

Kod neden böyle yazılmıştır?

Neden MonadWriter type sınıfını oluşturmak için uğraşasınız? Sebep monad transformatörleri ile yapmak. Doğru bir şekilde fark ettiğiniz gibi, Writer öğesini uygulamanın en basit yolu bir çiftin üstüne yeni tip bir sarıcı gibidir:

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 ()

bu sadece girişini kaydeder. Şimdi, günlüğe kaydetme özelliklerine sahip bir monad istediğinizi, ancak aynı zamanda başka bir şey yaptığını varsayalım - bir ortamdan da okuyabileceğini söyleyin. Bunu olarak uygulardın

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

Artık yazar ReaderT monad transformatörünün içinde olduğundan, çıktıyı günlüğe kaydetmek istiyorsanız, tell w kullanamazsınız (çünkü yalnızca açılmamış yazarlarla çalışır) ancak tell işlevini kullanarak ReaderT işlevini "kaldıran" lift $ tell w kullanmak İç yazar monadına erişebileceğini. İki katman transformatörü istiyorsanız (hata işleme de eklemek istediğinizi söyleyin), lift $ lift $ tell w kullanmanız gerekir. Bu hızla hantallaşıyor.

Bunun yerine, bir tür sınıfı tanımlayarak, bir yazıcının etrafındaki herhangi bir monad transformatör sargısını yazıcının kendisinin bir örneğine yapabiliriz. Örneğin,

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

yani, w bir monoid ise ve m bir MonadWriter w ise, ReaderT r m ayrıca bir MonadWriter w olur. Bu, tell işlevini doğrudan monad transformatöründen açıkça kaldırmakla uğraşmak zorunda kalmadan doğrudan dönüştürülmüş monad üzerinde kullanabileceğimiz anlamına gelir.

115
Chris Taylor

"Writer" yapıcısı yerine "writer" adı verilen bir işlev kullanılabilir Değişiklik: 

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

için:

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

4
Marcus

Benzer bir mesaj aldım/ "Bir kaç Monad'lar için" çevrimiçi Haskell editörünü kullanarak repl.it

İçe aktarma işlemini değiştirdim:

import Control.Monad.Writer

için:

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

Bu yüzden kodum şimdi şöyle görünmeye çalışıyor ( Kwang's Haskell Blog 'dan 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

0
Simon Dowdeswell