モナドについて何も知らない人にモナド型クラスを説明するのに最適なモナド型は何ですか? 標準的な Haskell ライブラリから何かを使用する必要がありますか、それとも新しいタイプを作成する必要がありますか?
4 に答える
モナドを動機付ける最善の方法は、モナドの構造を持つ組み込みドメイン固有言語がいくつあるかを示すことだと思います。
- リスト内包表記はその明白な例です。
.then
JS Promises はバインド操作として機能するモナドです- Groovyの
?.
オペレーター - オブジェクト指向言語の多くの「流暢な」インターフェースはモナディックです
モナドを使用すると、6502 のアセンブラをプログラムリンクまたは BASIC コードリンクに埋め込むことができます。
モナド パターンを使用すると、計算の重要な詳細に集中するコードから不要な複雑さを取り除くことができます。
モナドパターンを理解することは、独自の EDSL を作成したい場合に役立ちます。
モナドは (私のMaybe
意見では) 最も理解しやすいものです。Maybe
(単純な) 代数型の概念を理解すれば、モナドがどのように機能するかを理解するのはかなり簡単です。
のコンストラクタを理解するのに苦労している場合Maybe
は、本質的に同じことを行うクラスを書くことができます。
class Maybe(object):
def __init__(self, value=None):
self.__just = value
def just(self):
if self.isJust():
return self.__just
else:
raise ValueError('None')
def isJust(self):
return self.__just is not None
def fmap(self, f):
if self.isJust():
return Maybe(f(self.just()))
else:
return Maybe()
def bind(self, fM):
"""fM must return a value of type Maybe"""
if self.isJust():
return fM(self.just())
else:
return Maybe()
def __repr__(self):
if self.isJust():
return 'Just ({})'.format(self.just())
else:
return 'Nothing'
def head(some_list):
if len(some_list) == 0:
return Maybe()
else:
return Maybe(some_list[0])
def idx(some_list, i):
if idx < len(some_list):
return Maybe(some_list[i])
else:
return Maybe()
print head([1, 2, 3]).bind(
lambda x: Maybe(2 * x)).bind(
lambda x: Maybe(x + 1)).bind(
lambda x: Maybe(x + 3))
print head([[1, 2, 3]]).bind(
lambda xs: idx(xs, 0)).bind(
head).bind(
lambda x: 2 * x)
print head([[1, 2, 3]]).bind(
lambda xs: idx(xs, 1)).bind(
head).bind(
lambda x: 2 * x)
このコードは出力されます
Just (6)
Nothing
Nothing
これは Haskell のモナドと同じ機能 (多かれ少なかれ)Maybe
を持ち、クラスを使用して Python で再実装されています。Haskellのreturn
関数はコンストラクターに置き換えられ、 に>>=
置き換えられ.bind
ます。
実際の使用からモナドパターンを発生させることが重要だと思います。いくつかの型を選び、モナド パターンによって自然に表現される問題に誰かを向けることは啓発的です。
私の頭の上で、 の利点について議論し、Maybe
明らかに結果として生じるネストされたエラー処理について誰かに心配させてから、その方法について話すのは非常に簡単です。
case f x of
Nothing -> Nothing
Just y -> case g y of
Nothing -> Nothing
Just z -> case h z of
Nothing -> Nothing
Just q -> case r q of
Nothing -> Nothing
Just end -> end
実際、Haskell で抽象化できる非常に一般的なパターンです。
Config
次に、構成と、データ型を多くの関数に渡して操作することがいかに役立つかについて説明します。次のようなコードを書くのは簡単です
go config in =
let (x, y) = f config $ g config $ h config in
in finally config x (we'reDone config y)
しかし、これも Haskell で非常に一般的なパターンであり、煩わしくなりますが、冗長性を軽減するための一般的な戦略があります。
最後に、次のような自己同相の連鎖としての状態の突然変異について話します。
let state4 = (modify4 . modify3 . modify2 . modify1 :: State -> State) state0
また、中間ステップから情報を取得することを許可せずに「変更チェーン」を前もって修正しながら(少なくとも、状態と一緒にスレッド化することなく)、それはかなり面倒です。
繰り返しますが、これは Haskell では奇妙な名前の一般的な抽象化パターンによって非常に均一に解決できます。モナドについての話を聞いたことがありますよね?
私が得たより深い洞察の 1 つは、モナドは、構成可能な命令型プログラミング言語と見なすことができるということでした。したがって、彼らと一緒に「言語」を構築する必要があるかもしれません。
彼らのためにプログラミング言語を作るのは良い視点だと思います。
たとえば、最初に状態を追加します
import Data.Map as M
import Control.Monad
import Control.Monad.State
data Variable = IntV Int | StringV String ..
type Context = M.Map String Variable
type Program a = State Context a
次に、ロギングを追加します。
type Program a = WriterT [文字列] (State Context) a log x = tell [x]
次に、例外を追加します。
タイプ プログラム a = WriterT [文字列] (StateT コンテキスト どちらか) a
次に、継続などを追加します。
このようにして、モナドを使用して問題に最適な環境を構築できることを彼らに示すことができます。この後、彼らが興味を持っている場合は、モナドの構造とそれらがどのように構築されているかを示します。
たとえば、最初に Maybe モナドを表示します。最初に、純粋なラムダ バージョンを提供します。
data Perhaps a = Sure a | Nope
next (Sure a) f = f a
next (Nope) f = Nope
ラムダを使用して計算を連鎖させる方法を示します。
small x | x < 100 = Sure x
| otherwise = Nope
between x y z | x < z < y = Sure z
| otherwise = Nope
small 10 `next` (\b -> between 5 20 b)
次に、これを do 表記に変換する方法を示します。
small x `next` (\b -> between 5 20 b
`next` (\c -> between 10 14 c))
次のように記述できれば便利だと思います。
small x -> \b ->
between 5 20 b -> \c ->
between 10 14 c
次に do 記法を導入します。
b <- small x
c <- between 5 20 b
between 10 14 c
今、彼らは do 記法を発明し、そこから他のモナドを説明することができます。