Saya baru mengenal pemrograman fungsional dan baru-baru ini belajar di Learn You a Haskell , tetapi ketika saya melewati bab ini , saya terjebak dengan program di bawah ini:
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)
Saya menyimpan baris-baris ini dalam file .hs dan tetapi gagal mengimpornya ke ghci saya yang mengeluh:
more1.hs:4:15:
Not in scope: data constructor `Writer'
Perhaps you meant `WriterT' (imported from Control.Monad.Writer)
Failed, modules loaded: none.
Saya memeriksa jenis dengan perintah ": info":
Prelude Control.Monad.Writer> :info Writer
type Writer w = WriterT w Data.Functor.Identity.Identity
-- Defined in `Control.Monad.Trans.Writer.Lazy'
Dari sudut pandang saya, ini seharusnya menjadi sesuatu seperti "Newtype Writer wa ..." jadi saya bingung tentang cara memberi makan konstruktor data dan mendapatkan Writer.
Saya kira ini mungkin masalah terkait versi dan versi ghci saya adalah 7.4.1
Jawaban:
Paket
Control.Monad.Writer
tidak mengekspor konstruktor dataWriter
. Saya rasa ini berbeda ketika LYAH ditulis.Menggunakan kelas tipe MonadWriter di ghci
Sebaliknya, Anda membuat penulis menggunakan
writer
fungsi tersebut. Misalnya, dalam sesi ghci yang bisa saya lakukanSekarang
logNumber
adalah fungsi yang menciptakan penulis. Saya bisa menanyakan tipenya:Yang memberi tahu saya bahwa tipe yang disimpulkan bukanlah fungsi yang mengembalikan penulis tertentu , melainkan apa pun yang mengimplementasikan
MonadWriter
kelas tipe. Saya sekarang dapat menggunakannya:(Input sebenarnya memasukkan semua dalam satu baris). Di sini saya telah menentukan tipe
multWithLog
menjadiWriter [String] Int
. Sekarang saya bisa menjalankannya:Dan Anda melihat bahwa kami mencatat semua operasi perantara.
Mengapa kode ditulis seperti ini?
Mengapa repot-repot membuat
MonadWriter
kelas tipe? Alasannya ada hubungannya dengan trafo monad. Seperti yang Anda sadari dengan benar, cara termudah untuk mengimplementasikanWriter
adalah sebagai pembungkus tipe baru di atas pasangan:Anda dapat mendeklarasikan instance monad untuk ini, lalu menulis fungsinya
yang hanya mencatat masukannya. Sekarang misalkan Anda menginginkan monad yang memiliki kemampuan logging, tetapi juga melakukan sesuatu yang lain - katakanlah ia juga dapat membaca dari lingkungan. Anda akan menerapkan ini sebagai
Sekarang karena penulis berada di dalam
ReaderT
trafo monad, jika Anda ingin mencatat keluaran Anda tidak dapat menggunakantell w
(karena itu hanya beroperasi dengan penulis yang tidak terbungkus) tetapi Anda harus menggunakanlift $ tell w
, yang "mengangkat"tell
fungsi melaluiReaderT
sehingga dapat mengakses penulis batin monad. Jika Anda menginginkan transformator dua lapisan (katakanlah Anda ingin menambahkan penanganan kesalahan juga) maka Anda perlu menggunakanlift $ lift $ tell w
. Ini dengan cepat menjadi berat.Sebaliknya, dengan mendefinisikan kelas tipe kita dapat membuat pembungkus trafo monad di sekitar penulis menjadi instance penulis itu sendiri. Sebagai contoh,
yaitu, jika
w
monoid, danm
merupakan aMonadWriter w
, makaReaderT r m
juga aMonadWriter w
. Artinya kita dapat menggunakantell
fungsi tersebut secara langsung pada monad yang ditransformasikan, tanpa harus repot-repot mengangkatnya secara eksplisit melalui trafo monad.sumber
mtl
dari versi mayor 1. * ke 2. *, tak lama setelah LYAH dan RWH ditulis. Waktu yang sangat disayangkan yang menyebabkan dan menyebabkan banyak kebingungan di kalangan pemula.Control.Monad.Trans.Writer
. Selain itu, tipelogNumber
inilogNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m a
untuk saya.mtl
menginstal library (yang mungkin berarti Anda memiliki instalasi dasar GHC, seperti minGHC, bukan Platform Haskell). Dari perintah run yang cepatcabal update
dancabal install mtl
kemudian coba lagi.writer
digunakan sebagai penggantiWriter
sebagai yang terakhir, nilai ctor, tidak diekspor oleh modul, sedangkan yang pertama adalah, dan dapat digunakan untuk membuat nilai yang sama yang akan Anda buat dengan ctor, tetapi tidak tidak mengizinkan pencocokan pola.Fungsi yang disebut "penulis" tersedia sebagai pengganti konstruktor "Penulis". Perubahan:
logNumber x = Writer (x, ["Got number: " ++ show x])
untuk:
logNumber x = writer (x, ["Got number: " ++ show x])
sumber
Saya mendapat pesan serupa dari mencoba LYAH "For a few Monads More" menggunakan editor Haskell online di repl.it
Saya mengubah impor dari:
untuk:
Jadi kode saya sekarang berfungsi seperti ini (dengan inspirasi dari Blog Haskell Kwang ):
Kode saat ini dapat dijalankan di sini
sumber