モナドのないHaskell

haskellを研究するプログラマヌは遅かれ早かれモナドのような理解できない抂念に出くわすでしょう。 倚くの人にずっお、この蚀語の知識はモナドで終わりたす。 倚くのモナドガむドがあり、新しいガむドが垞に衚瀺されたす1。 モナドを理解しおいる少数の人は、知識を慎重に隠し、モナドを内機胜ず自然倉換の芳点から説明したす2。 経隓豊富なプログラマヌは、䞖界の確立された絵の䞭にモナドの堎所を芋぀けるこずができたせん。



その結果、JavaプログラマヌはHaskellを笑うだけで、100䞇行の゚ンタヌプラむズプロゞェクトからは目を離したせん。 C ++開発者は、超高速アプリケヌションにパッチを適甚し、さらにスマヌトなポむンタヌを䜜成したす。 Web開発者は、css、xml、およびjavascriptの䟋ず巚倧な仕様に目を通したす。 そしお、暇なずきにhaskellを勉匷する人は、モナドず呌ばれる乗り越えられない障害に盎面しおいたす。



それで、 モナドなしでHaskellでプログラムする方法を孊びたす。





これを行うには、少しの空き時間、おやすみなさい、お気に入りの飲み物のマグカップ、ghcコンパむラが必芁です。 Windowsおよびmacosでは、haskellプラットフォヌム3パッケヌゞに含たれおいたす。Linuxナヌザヌはリポゞトリからghcをむンストヌルできたす。 Prelude>で始たるサンプルコヌドは、むンタラクティブなむンタヌプリタヌghciでチェックできたす。



ハブにも同様の蚘事がありたした4が、I / Oのすべおの入出力に぀いおは説明しおいたせんが、䜿甚できる既補のテンプレヌトを提䟛しおいたす。



次のステップに進む-オペレヌタヌ



遠くから始めたしょう。 Haskellのすべおの蚈算は、「副䜜甚あり」ず「副䜜甚なし」に分けられたした。 最初の䟋には、たずえば、入力/出力デバむスからの曞き蟌み/読み取り、途䞭で゚ラヌが発生する可胜性がある蚈算などが含たれたす。 「副䜜甚なし」には、数倀の远加、文字列の接着、数孊的蚈算、「玔粋な」関数などの操䜜が含たれたす。



玔粋な関数は、他のすべおのプログラミング蚀語の関数が結合するのず同じ方法で結合したす。

Prelude> show (head (show ((1 + 1) -2))) '0'
      
      







副䜜甚のあるプログラムを䜜成するために、特別な挔算子が䜜成されたした

 >>=
      
      





それを「接続」eng。bindず呌びたしょう。 すべおのI / Oアクションはそれらに接着されおいたす。

 Prelude> getLine >>= putStrLn asdf asdf
      
      





この挔算子は、副䜜甚のある2぀の関数を入力ずしお受け入れ、巊の関数の出力は右ぞの入力ずしお機胜したす。



むンタプリタコマンドで関数のタむプを芋おみたしょうt

 Prelude> :t getLine getLine :: IO String Prelude> :t putStrLn putStrLn :: String -> IO ()
      
      







そのため、getLineは䜕も受け入れず、IO String型を返したす。



タむプ名に2぀の単語が含たれおいるずいう事実は、このタむプが耇合であるこずを瀺しおいたす。 そしお、最初に来る蚀葉は、タむプビルダヌず呌ばれたす。他のすべおはこのビルダヌのパラメヌタヌです䞍協和音が聞こえるのは知っおいたすが、そうすべきです。



この堎合、IOずいう単語は単なる副䜜甚を意味し、挔算子=によっお砎棄されたす。 副䜜甚の他の「指暙」の䟋ずしお、人気のあるタむプStateを挙げるこずができたす。これは、関数が䜕らかの皮類のステヌトを持぀こずを意味したす。



putStrLnに進みたしょう。 この関数は文字列を入力ずしお受け取り、IOを返したす。 IOを䜿甚するず、すべおが明確になり、副䜜甚があり、はHassalのsyssal voidの類䌌物です。 ぀たり 関数は入力/出力で䜕かを行い、空の倀を返したす。 ずころで、すべおのHaskellプログラムはこのIOで終了する必芁がありたす。



そのため、「接続」挔算子は、最初の匕数から結果を取埗し、副䜜甚むンゞケヌタを切り取り、2番目の匕数で発生したこずを枡したす。 これは耇雑に思えたすが、Haskellの半分はこの単䞀のステヌトメントでサポヌトされ、すべおの入力/出力はそれを䜿甚しおプログラムされたす。 それは非垞に重芁であり、蚀語のロゎに远加されたほどです。



接着された関数の返された倀ず受け入れられた倀が䞀臎しない堎合はどうなりたすか ラムダ関数が助けになりたす。 たずえば、入力ずしおパラメヌタヌを受け入れるだけですが、䜕も行いたせん。

 Prelude> (putStrLn " 1") >>= (\a -> putStrLn " 2") >>= (\b -> putStrLn " 3")  1  2  3
      
      





今埌、「=」挔算子の優先床は非垞に䜎く、必芁に応じお、この䟋では括匧なしで実行できたす。 さらに、この䟋のように、匕数がラムダ関数内で䜿甚されおいない堎合は、_で眮き換えるこずができたす。



最初の䟋を完党に同等に曞き換えたすが、ラムダ関数を䜿甚したす。

 Prelude> getLine >>= \a -> putStrLn a asdf asdf
      
      





ラムダ関数を䜿甚しお画面に文字列を衚瀺するずき、1぀の倉数を受け入れるこずを明瀺的に瀺し、その䜿甚方法を明瀺的に蚘述したした。



「倉数」ず蚀いたしたか



はい、倉数に぀いお話したしょう。 ご存じのように、Haskellには倉数はありたせん。 ただし、リストを芋るず、倚くの割り圓おが衚瀺されたす。



䞊蚘のコヌドでは、aずbは倉数に非垞に䌌おいたす。 他の蚀語ず同様に参照するこずもできたす。 ただし、これらのaずbは、呜什型蚀語の倉数ずは倧きく異なりたす。



すべおの呜什型プログラミング蚀語では、倉数は名前付きのメモリ領域です。 Haskellでは、aやbのようなものは名前付きの匏ず倀です。



䟋を挙げお、これらの違いを瀺したす。 次のCコヌドを怜蚎しおください。

 a = 1; a = a + 1; printf("%d",a)
      
      





すべおが透明で、結果は予枬可胜です。



Haskellでも同じこずを行いたす。

 Prelude> let a = 1 Prelude> let a = a + 1 Prelude> print a ^CInterrupted.
      
      





コヌドの実行は終わりたせん。 1行目では、aを1ずしお定矩したす。2行目では、a + 1ずしお定矩したす。2行目を読み取るずき、むンタヌプリタヌはaの前の倀を忘れ、この堎合、a自䜓を再床決定したす。 さお、この再垰的な定矩は決しお蚈算されたせん。



蚘憶の指定された領域に関しおは-Haskellにありたすが、これはたったく異なる話です。



この蚭蚈を䜿甚するず、「connect」挔算子のいく぀かの呌び出しを通じおパラメヌタヌを枡すこずができたす。

 Prelude> getLine >>= \a -> putStrLn " :" >>= \_ -> putStrLn a asdf  : asdf
      
      







実際のコヌド



ここで、秘密の知識を䜿甚しお、珟実のものを曞きたす。 ぀たり、ナヌザヌからデヌタを受信するプログラムは、ナヌザヌに察しお䜕らかのアクションを実行し、結果を画面に衚瀺したす。 プログラムを別のファむルに曞き蟌み、マシンコヌドにコンパむルしたす。



ファむルにtest.hsずいう名前を付けたす。

 main = putStrLn "  :" >>= \_ ->       getLine >>= \a ->       putStrLn "   :" >>= \_ ->       putStrLn (show ((read a)^2))
      
      





コンパむル

 ghc --make test.hs
      
      





実行

 $ ./test   : 12    : 144
      
      





read関数は、文字列を目的の型の倀に解析しようずしたす。 圌女が掚枬するタむプは別の話です。 show関数は、任意のタむプの倀を文字列に倉換したす。



読み取り関数は安党ではありたせん;文字を䞎えお数字の解析を芁求するず、゚ラヌが発生したす。 これにこだわる぀もりはありたせん。このケヌスには安党なモゞュヌルがあるこずだけに蚀及したす。



玔床の混合物



それずは別に、副䜜甚コヌドから玔粋な関数を呌び出す方法に぀いおの疑問が生じたす。



䞊蚘の䟋では、玔粋な関数は単にIO関数の匕数ずしお蚘述されおいたす。 倚くの堎合、これで十分ですが、垞にではありたせん。



クリヌンコヌドを呌び出す方法は他にもありたす。



これらの最初のものは、クリヌンなコヌドから副䜜甚コヌドぞの暎力的な倉換です。 実際、玔粋なコヌドは副䜜甚の特殊なケヌスず芋なすこずができるため、この倉換は危険をもたらしたせん。 そしお、return関数を䜿甚しお実装されたす

 main = putStrLn "  :" >>= \_ ->       getLine >>= \a ->       putStrLn "   :" >>= \_ ->       return (show ((read a)^2)) >>= \b ->       putStrLn b
      
      





コンパむル、怜蚌、プログラムは以前ず同じように機胜したす。



もう1぀の方法は、let ... in ... Haskelコンストラクトを䜿甚するこずです。倚くのマニュアルでは、十分な泚意が払われおいるので、止めたせん。既補の䟋を挙げたす。

 main = putStrLn "  :" >>= \_ ->       getLine >>= \a ->       putStrLn "   :" >>= \_ ->       let b = (show ((read a)^2)) in       putStrLn b
      
      







もっず砂糖が必芁



蚀語開発者は、構造が䞀般的であるこずを認識しおいたす

 >>= \_ ->
      
      





したがっお、それらを瀺すために、挔算子を導入したした

 >>
      
      





コヌドを曞き換えたす

 main = putStrLn "  :" >>       getLine >>= \a ->       putStrLn "   :" >>       let b = (show ((read a)^2)) in       putStrLn b
      
      





それでもう少しきれいになりたした。



しかし、もっずクヌルなトリックがありたす-構文糖は「する」 

 main = do    putStrLn "  :"    a <- getLine    putStrLn "   :"    let b = (show ((read a)^2))    putStrLn b
      
      





必芁なもの だから、すでに生きるこずができたす。



巊揃えに限定されたdoブロック内では、次の眮換が行われたす。

 a <- abc   abc >>= \a -> abc   abc >> let a = b   let a = b in do
      
      





「do」衚蚘により、構文は最新のすべおのプログラミング蚀語の構文ず非垞によく䌌おいたす。 それにもかかわらず、内郚では、クリヌンなコヌドず副䜜甚コヌドを分離するためのよく考えられたメカニズムがありたす。



興味深い違いは、returnステヌトメントの䜿甚です。 ブロックの䞭倮に挿入でき、関数の実行を䞭断せず、混乱を招く可胜性がありたす。 しかし実際には、ブロックの最埌でIO関数から玔粋な倀を返すためによく䜿甚されたす。

 get2LinesAndConcat:: IO String get2LinesAndConcat = do    a <- getLine    b <- getLine    return (a + b)
      
      







真空䞭の球



次に、別の関数でクリヌンなコヌドを取り出したす。 同時に、最埌に䞍足しおいるタむプ眲名を敎理したしょう。

 main :: IO () main = do    putStrLn "  :"    a <- getLine    putStrLn "   :"    let b = processValue (read a)    putStrLn (show b) processValue :: Integer -> Integer processValue a = a ^ 2
      
      





重芁な点は、副䜜甚のI / OコヌドはI / Oコヌドからのみ実行できるこずです。 ただし、クリヌンコヌドはどこからでも実行できたす。



したがっお、玔粋な機胜的䞖界は、副䜜甚に関連するすべおのものから厳密か぀確実に分離されたす。 processValueの内郚では、あらゆるものを怜蚎し、あらゆるロゞックを実装できたす。 しかし、100䞇行のコヌドのロゞックがそこから呌び出されたずしおも、どの入力倀に察しおも、出力は垞に同じであるず確信できたす。 そしお、そこに枡されたパラメヌタヌは決しお損なわれるこずはないので、さらに安党に䜿甚できたす。



スタむルガむドでは、副䜜甚コヌドの䜿甚を最小限に抑え、玔粋な機胜に最倧限の機胜をもたらすこずをお勧めしたす5。 ただし、プログラムがI / Oを実行するように蚭蚈されおいる堎合は、必芁な堎所で䜿甚するこずを避けないでください。 原則ずしお、そのような堎合、補助機胜が必芁であり、これは玔粋な堎合がありたす。 経隓豊富なHaskellプログラマヌは、呜什型蚀語ず比范しおIOコヌドの優れたサポヌト性を認識しおいたすこのステヌトメントはSimon Peyton Johnesによるものですが、盎接リンクは芋぀かりたせんでした。



玔粋な関数には、パフォヌマンスの1぀の偎面がありたす。 叀兞的な䟋を芋おみたしょう。倚くのフィヌルドを持぀耇雑な「埓業員」構造を関数に枡したす。 そのため、siずの類掚により、コヌドの効率はポむンタヌでこのパラメヌタヌを枡すこずに匹敵し、siではスタックを通過するだけで元の構造の耐性が保蚌されるため、信頌性はパラメヌタヌをスタックに枡すこずに匹敵したす。



䜕蚀っおるの



「このコヌドはひどく、䞍合理に耇雑で、他のすべおの蚀語のりォヌムチュヌブセマンティクスずの共通点が少なすぎたす。c/ c ++ / c/ java / pythonなどで十分です。」



たあ、これにはいく぀かの真実がありたす。 ここで、あなたが恐ろしいず思うこずを決定する必芁がありたすきれいなコヌドたたはこのメカニズムの特定の実装から副䜜甚を分離したす。



そのようなメカニズムをよりシンプルで理解しやすいものにする方法を知っおいるなら、それを䞖界のコミュニティに䌝えおください Haskellコミュニティは非垞にオヌプンでフレンドリヌです。 定期的に採甚されおいる新しい暙準のドラフトでは、あらゆる提案が考慮されおおり、本圓に䟡倀がある堎合は、確実に受け入れられたす。



「Pythonでもすべおが良い、あなたは副䜜甚に執着しおいる」ず思うなら、誰もあなたが奜きなツヌルを䜿うこずを気にしたせん。 私からは、Haskellが開発を本圓に単玔化し、コヌドをより理解しやすくするこずを付け加えるこずができたす。 これたたはその逆を確認する唯䞀の方法は、Haskellで曞くこずです



次に行く堎所



さらなる研究のため、 たたはこの蚘事の代わりに 、蚘事「haskellの゜フト入門」6、特にその翻蚳7をお勧めしたす。



さらに、もちろん、他の蚘事も適しおいたす8。 倚くのガむドが曞かれおいたすが、それらはすべお異なる芳点から同じこずを説明しおいたす。 残念ながら、ロシア語に翻蚳された情報はほずんどありたせん。 豊富なマニュアルがあるにもかかわらず、蚀語は単玔であり、その説明ず暙準ラむブラリの説明は270ペヌゞしか必芁ありたせん9。



かなり倚くの情報も暙準ラむブラリのドキュメントに含たれおいたす10。



この蚘事が誰かを助けたり、単に面癜そうだず思ったら、コメントや批刀を歓迎したす。



ps Haskellの䞖界で「タむプビルダヌ」ず呌んだものは、「 タむプコンストラクタヌ 」ず呌ばれたす。 これは、OOPから取られた「デザむナヌ」ずいう蚀葉の意味を忘れやすくするために行われたす。これらは完党に異なるものです。 この状況は、型コンストラクタヌに加えお、OOPずは関係のないデヌタコンストラクタヌもあるずいう事実によっお悪化したす。



参照資料



  1. www.haskell.org/haskellwiki/Monad_tutorials_timeline
  2. http://en.wikipedia.org/wiki/Monad_(category_theory
  3. hackage.haskell.org/platform
  4. habrahabr.ru/blogs/Haskell/80396
  5. www.haskell.org/haskellwiki/Avoiding_IO
  6. www.haskell.org/tutorial
  7. www.rsdn.ru/article/haskell/haskell_part1.xml
  8. www.haskell.org/haskellwiki/Tutorials
  9. www.haskell.org/definition/haskell98-report.pdf
  10. www.haskell.org/ghc/docs/7.0.3/html/libraries




updネタバレ



コメントで正しく促されたので、モナドのマニュアルの名前の遞択は完党に成功しおいたせん。 モナドのトピックは公開されおいないため、控えめな印象が残っおいたす。



そのため、「モナド」ずいう蚀葉は䞀連の挔算子です

 >>= >> return fail
      
      





そしお、それらが定矩されおいるあらゆるタむプのデヌタ。 たずえば、IO。



この蚀葉の呚りでは、あたり良いオヌラは発達しおいたせんが、実際には秘密の意味はありたせん。 これは、 モナドなしで説明できるプログラミングパタヌンの名前です。



upd2

Afiskonは興味深いプレれンテヌションぞのリンクを提䟛したした

Haskellに぀いお 。



All Articles