手続きに帰れ / Back to Procedure

> Back to Procedure / 手続きに帰れ (2)(U)
>
> エンチャント
>
> 手続き的でない構文は,それらのコントローラーのアンタップ・ステップにアンタップしない。

## TL; DR ～ 寺生まれの do さん ～

do 式はすごい,改めてそう思った。

## 前置き

むくむくと純粋関数型言語への興味が湧いてきた最近の私は,とりあえず PureScript を勉強することにした。この記事は一種の備忘録である。

ハローワールドレベルのごく小規模なプログラム, JavaScript (Node.js) による手続き的なそれを PureScript のモナドチェーンによる書き方に置き換え,それを構文糖である do 式を使用して書き換えることで手続き的な表現に戻せることを確かめよう。

## 元のプログラム

```js
var readlineSync = require('readline-sync');

function main() {
  console.log("Please tell me your first name?");
  var firstName = readlineSync.question("> ");
  console.log("Please tell me your last name?");
  var lastName = readlineSync.question("> ");
  console.log("Hello, " + firstName + " " + lastName + "!");
}

main();
```

readline-sync を使用して簡単な標準入力を実現していることをのぞけば,ごくごく普通の挨拶プログラムである。プログラミング教育の 2 時限目で取り上げられそうな題材だ。

## PureScript で書く ～ モナドチェーンによる ～

readline-sync を FFI で PureScript から利用できるようにするが,先人の尻馬に乗ったばかりであまり大したこともしていないので省略する。[こちら](https://qiita.com/goldarn_ring/items/e3cc607dd63e57d91fe5)を参照。今回は`question`という名前の`String -> Effect String`型の函数として定義した。

<details><summary>詳細</summary><div>

```js:ReadlineSync.js
"use strict";

// module Effect.ReadlineSync

var readlineSync = require('readline-sync');

exports.question = function (query) {
  return function() {
      return readlineSync.question(query);
  }
}
```

```haskell:ReadlineSync.purs
module Effect.ReadlineSync (question) where

import Effect (Effect)

foreign import question :: String -> Effect String
```

</div></details>

### `Effect Unit` (`IO ()`) とはなにであるか

エントリーポイントである`main`の型は`Effect Unit`であるという。

`Unit`は具体型だ。これは直系の親である Haskell では`()`と表現されているもので,`unit`以外の値をもたない,<ruby>単集合<rt>Unit Set</rt></ruby>の型だ。これは C 直系言語で言うところの`void`のような扱いを受けていると考えていいだろう。

`Effect`は Haskell では`IO`にあたる多相型で,具体型を一つとって別の具体型に変わる。ある意味では,オブジェクト指向でよく出てくる`ArrayList<String>`のようなジェネリッククラスに近い。`Effect Unit`なら,`Unit`の属性を帯びた`Effect`みたいな感じだ。そして,`Effect`はかの悪名高いモナドでもあり,作用を意味している。

ところで,いまの表現は巷のメタファーとは主従が逆転している。たいてい,[モナド`m a`は`m`の中に`a`が入っているかのように説明される](https://qiita.com/suin/items/0255f0637921dcdfe83b)。

たしかに`Maybe a`のようなモナドは`a`型の値が入った箱のようなものと思ってもそう間違いではない。

が,`Effect a`というモナドは,`a`型の値が入っていると言うよりかは`a`型の値を返す関数 (ルーチン) によって表現されると考えるべきだ。これをアクションという。

だから,それらの値を使って何かをしたいという欲求は,関数合成のような仕組みで実現される。モナドは文脈の合成を抽象化したものと言えるかもしれない。

純粋でありながら作用を扱えるというのは,このアクションとモナドの性質による。

たとえば,調理という行為は作用を起こすといえる。材料が消失し,料理ができあがる。これは副作用だ。あげく,レシピに “ホットケーキミックスが足りない場合,小麦粉で代用” とか書いてあったら目も当てられない。できあがる料理はホットケーキミックスの多寡によって変わってしまう。こんなの,純粋関数型言語で許せるわけがない。

しかし,レシピは作用を起こさない。当たり前の話である。レシピというただの情報が勝手に料理を生成し始めたらそんなもん[収容](http://scp-jp.wikidot.com/)案件だ。

そして,レシピは自由に組み合わせることができる。ご飯の炊き方,みそ汁の作り方,魚の焼き方。これらを合わせて朝食のレシピとなる。レシピを組み合わせても当然作用など起きようがないし,たとえレシピが条件で分岐していても,出来上がる合成レシピが変わることはない。合成されたレシピも条件で分岐するよう書かれるだけの話だ。つまり同じ入力に対しては同じ出力を返す,純粋な計算である。

この “組み合わせる” という操作を抽象化して,様々な文脈に応用するのがモナドという概念なのだ……と理解している。

### モナドはいかにして合成するか

文脈の合成に便利な演算子をふたつ紹介。

まずは<ruby>束縛<rt>Bind</rt></ruby>演算子`>>=`。左オペランドに`m a`なモナド,右オペランドに`a -> m b`なモナド函数をとって`m b`なモナドを返す。手元のモナドを適切な函数の引数に束縛してやれば,まるで値をコンベアに乗せて加工していくようにつないでいける。

そして<ruby>適用二次<rt>Apply Second</rt></ruby>演算子`*>`。モナドをふたつとって合成し,右オペランドの方を返す。一見無意味に思えるが,左オペランドがアクション系のモナドだった場合,宙ぶらりんだがそれが実行されるわけだ。

今回は使用しないが,以下のような演算子や函数も便利だ。

<ruby>写像<rt>Map</rt></ruby>演算子`<$>`。左オペランドに`a -> b`な函数,右オペランドに`m a`なモナドをとって`m b`なモナドを返す。JavaScript 配列の`map`メソッドを使った経験はあるだろうか。この演算子は,まさしくそれだ。

<ruby>適用<rt>Apply</rt></ruby>演算子`<*>`。左オペランドに`m (a -> b)`な関数モナド,右オペランドに`m a`なモナドをとって`m b`なモナドを返す。カリー化された函数に写像演算子で引数を渡すと返る函数もモナドになるので,次の引数は適用演算子で渡す必要がある。`add <$> someMonadA <*> someMonadB`ってな具合だ。

`pure`。`a`型の値を引数にとって`m a`なモナドを返す。各モナドごとに実装されていて,例えば`pure 1.0`とは<ruby>純然たる<rt>Pure</rt></ruby>`1.0`…というように “最も単純なモナド” を作り出す函数といえる。ちなみに`Effect`モナドの場合は “実際にはなんの副作用も起こさず (引数として与えた値) を返すだけのアクション” を作り出す。

こんなところだろうか。ちなみに,ここまでモナドモナドと言ってきたが,モナドよりもっと広い概念の演算子 / 函数だったり。Haskell では`*>`のモナド専用バージョン`>>`と`pure`のモナド専用バージョン`return`があるらしい。

### 私はいかにして失明したか

少々長く説明しすぎた。`log`が`String -> Effect Unit`型,`question`が`String -> Effect String`型であることを考慮しながら書いていくと,つまりこういうことになる。

まず,最初の文を表示する。

```haskell
main = log "Please tell me your first name?"
```

つづいて,入力を促す。さっきのアクションは返り値を使わないので (っていうか`unit`だし) 適用二次演算子で捨てる。

```haskell
main = log "Please tell me your first name?" *> question "> "
```

入力された文字列は後で使うので,とりあえずラムダ式の引数に束縛する。`question "> "`の型が`Effect String`であるから,ラムダ式の型は自動的に`String -> Effect a`となるわけで,引数`firstName`の型は`String`だ。

```haskell
main = log "Please tell me your first name?" *> question "> " >>= (\firstName -> ...)
```

これをもう一度繰り返す。

```haskell
main = log "Please tell me your first name?" *> question "> " >>= (\firstName -> log "Please tell me your last name?" *> question "> " >>= (\lastName -> ...))
```

最後に,ラムダ式の引数を使って挨拶を表示。先述の通り,引数は単に`String`とみなせる。

```haskell
main = log "Please tell me your first name?" *> question "> " >>= (\firstName -> log "Please tell me your last name?" *> question "> " >>= (\lastName -> log ("Hello, " <> firstName <> " " <> lastName <> "!")))
```

出来上がったのを改めて眺めてみる。

```haskell
module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)
import Effect.ReadlineSync (question)

main :: Effect Unit
main = log "Please tell me your first name?" *> question "> " >>= (\firstName -> log "Please tell me your last name?" *> question "> " >>= (\lastName -> log ("Hello, " <> firstName <> " " <> lastName <> "!")))
```

うむ,長い。長過ぎる。そして一見して意図が掴みづらい。モナドを束縛するラムダ式は二つネストしているし,あまりにも読むのがきつい。こんなものレビューさせられたら PureScript ハラスメント略してピュアハラで訴えたくなるな。

## 改行してみる

失明したので少しでも読みやすくしよう。メソッドチェーンだって適宜改行するのだから,これも改行を入れたら見やすくなるかもしれない。

```haskell
main =
  log "Please tell me your first name?" *>
  question "> " >>= (\firstName ->
  log "Please tell me your last name?" *>
  question "> " >>= (\lastName ->
  log ("Hello, " <> firstName <> " " <> lastName <> "!")))
```

おや,こうすると意外と手続きとそう変わらない感じになってきた。

## SYNTAX SUGAR do式-DOSHIKI-「待たせたな!」

ここで do 式の登場だ! do 式の中では,束縛演算子はラムダ式の仮引数を左に移動して`<-`なんて代入っぽい見た目になり,適用二次演算子は単に消える。すると,

```haskell
main = do
  log "Please tell me your first name?"
  firstName <- question "> "
  log "Please tell me your last name?"
  lastName <- question "> "
  log ("Hello, " <> firstName <> " " <> lastName <> "!")
```

おお!

```js
function main() {
  console.log("Please tell me your first name?");
  var firstName = readlineSync.question("> ");
  console.log("Please tell me your last name?");
  var lastName = readlineSync.question("> ");
  console.log("Hello, " + firstName + " " + lastName + "!");
}
```

見比べてほしい。ほぼそのままじゃないか。最初のモナモナした感じが綺麗サッパリ消し去られた。これなら手続きで育ってきた人間も失明しない。さしずめ[こわくない PureScrtipt 入門](https://qiita.com/arowM/items/9ebfb7cafecd99290663)といったところだ。

ここで,モナドがどのように合成されるか,つまり do 式でいえばそれぞれの行の間になにをするかは,それぞれのモナドごとに違う。もちろん,モナドの設計者が適切に実装しているはずだ。たとえば`Maybe`なら失敗したら以降の行は全カット,`List`なら後段のリストに再帰的に実行,`Effect`なら先述したとおりある種の関数合成だ。

まるでこれはセミコロン`;`という演算子をオーバーロードしているかのようである。これをもってモナドは[「プログラム可能なセミコロン」](https://www.slideshare.net/tanakh/monad-tutorial)と呼ばれるわけですね。

## まとめ

モナドによるプログラミングをかじり do 式の正体を知った。

do 式の正体を知ることでややこしいとされる do 式のルールも丸暗記ではなく知識としてわかるようになる。[この記事](https://qiita.com/hiruberuto/items/8bbc0343bf794c368287#do%E5%BC%8F%E3%81%AE%E4%B8%80%E8%88%AC%E5%BD%A2)のルール 2 以降は正体を知った今なら説明できるはずだ。みんなもやってみよう。ルール 1 は単なる文法の決め事なので割愛。

しかしこんな長い素のモナドチェーンは二度と書きたくないと思いました。あらためて do 式に感謝。モナドチェーンだけでプログラミングなんてしたくないので[命令型風味の構文考えたりした奴は万死に値する](https://qiita.com/lex_naturalis/items/e62d1dca3f528d8378b7)という意見には賛同できない~~けど確かに Haskell で`return`って名前採用したのはアカンと思う。~~