Monad in TypeScript
Tags: typescript, haskell, scala, monad
Published: 2018-04-29 13:37:55

---
title:  Monad in TypeScript
published: true
description: Use monad in TypeScript, using of fp-ts
tags: TypeScript, Haskell, Scala, Monad
---


## tl; dr

- 普段はTypeScriptを書いているオタクが、すごいH本を読んだ📖

  - Haskellには便利な機能や考え方がたくさんあり、その一部はTypeScriptみたいなプログラミング言語でも表現できることがわかった

  - TypeScriptでHaskellみたいなことをしようと思うと、いわゆるモナドライブラリが便利であり、中でも[fp-ts](https://github.com/gcanti/fp-ts)が良さそうだった

以下にはfp-tsについて具体的な解説などをコードを交えて書く。

## はじめに

前職で、TypeScriptのコードに `type Either<Left, Right> = ...` みたいなtype aliasを書いていたエンジニアさんにHaskellを勧められ、[すごいH本](https://www.amazon.co.jp/すごいHaskellたのしく学ぼう！-ＭｉｒａｎＬｉｐｏｖａｃａ-ebook/dp/B009RO80XY)を読んでみた。

Haskellはすごかった。もの凄く強力なチカラを2つ持っている。ガチガチな静的型付けと、モダンな関数型プログラミング技法である。美しく、[型安全](https://postd.cc/what-is-type-safety/)で、無駄のない処理を書くことができる。

## 静的型で何でも表す

先ほどの`Either`というのも型クラス(Javaなどの一般的なクラスとは違い、どちらかというとインターフェースのようなもの)の一つで、失敗系と成功系の2つの文脈を一つの型として定義できる。HTTPリクエストならエラーメッセージとレスポンスをそれぞれ型として表現できるわけである。

こうした便利な型クラスは他にもたくさんあり、Haskellの他にScalaなんかにも存在する概念らしい。その名をモナドという。モナドをはじめとする"文脈を表現する型クラス"とその扱い方について詳しく知りたい方は[Monad](https://qiita.com/lagenorhynque/items/e68ada9ee2d63539eb45), [MTL style](https://qiita.com/ruicc/items/7512c990a1835bba444a)などで調べると良いと思うのだが、厳密さを追求すると高度な数学の話になってしまうし、何より私がわかっていないためこの記事で掘り下げて説明することはしない🙅けれど全くしないわけにもいかないため、他人の解説を引用させて頂いてお茶を濁したい。

> Functors, Applicatives, And Monads In Pictures - http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
> ![http://adit.io/imgs/functors/recap.png](http://adit.io/imgs/functors/recap.png)

画像中の箱は文脈を表している。この画像は、Haskellにおいて箱の種類ごとにどのような演算子(画像中の`<$>, <*>, >>=`)を使えば、文脈(=箱)を失わずに値を加工できるかを表している。この記事でこれらの記号自体は出てこないため気にしないでほしいのだが、以下の点だけは頭のどこかに記憶しておいてほしい。

- `Functor`, `Applicative` は箱の種類であり、`Monad`の親戚である。
- 箱は型で表現することができる
- 箱に入った値は、そのままでは箱無しの値を取る関数に渡すことができないため、特殊な関数・演算子を使う必要がある

## 関数型プログラミング

Haskellはいわゆる純粋関数型プログラミング言語であり、副作用のある処理は副作用のない処理と完全に切り離されている。副作用のない処理を書く場所では、値を目的の形式に加工するにあたって型のキャストや代入操作からは完全に解放され、変換処理そのものに集中できるような仕組みが用意されている。

## TypeScriptでHaskell, Scalaみたいなことをする

周知の事実であろうが、JavaScriptでもある程度の関数型プログラミングが可能である。[オライリー本も出ている。](https://www.amazon.co.jp/JavaScript%E3%81%A7%E5%AD%A6%E3%81%B6%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-Michael-Fogus/dp/4873116600)(内容は古くなってしまっている)。具体的には高階関数を使って副作用のない処理を繋げたり、関数をカリー化して他の関数に渡したりすることができる。

それに、TypeScriptやflowなんかを使えば静的型も使える。ならJavaScriptでもHaskell, Scalaっぽいことができるのでは？という考えに至る。そこで便利なのがモナドライブラリである。モナドライブラリは前述の文脈を表現する型・モナドとそれを扱うための関数などを提供してくれる。

さて、TypeScriptで利用可能なモナドライブラリについては[こちら](https://blog.mmmcorp.co.jp/blog/2016/10/10/compare-maybe-monad/)の記事が詳しい。しかし、こちらの記事の公開後にスター数・規模感共にTypeScriptのモナドライブラリでは最大の[fp-ts](https://github.com/gcanti/fp-ts)が現れた。今回の記事ではこちらの紹介をしていきたい。

## fp-ts

fp-tsは今話題の[PureScript](http://www.purescript.org/), そして[fantasy-land](https://github.com/fantasyland/fantasy-land)やScalaにインスパイアされたモナドライブラリらしい。他の有名なモナドライブラリが300 Star前後であるのに対して、規模感もあってか倍以上のStar数を持っている。

どんなことができるのかはREADMEの[Examples](https://github.com/gcanti/fp-ts#examples)にまとまっている。Free Monad, MTL StyleなどHaskellでメジャーな(?)テクニックから、`Option`, `Either` , `Reader` , `Writer` , `State`, `List` などの定番モナド、更にはHaskellでよく`Writer`の代わりに使えと言われている、デバッグに便利な `Trace` , 依存型を使って型安全に有限オートマトンを記述できる `IxIO` なんてものまである([2016年に発表された論文を使った実装](https://www.idris-lang.org/drafts/sms.pdf)のようだが、よくわからなかった…)。

モナドを中心に、fp-tsのいくつかの機能をdigってみる。

### Optionモナド

Optionモナドは、値がない可能性のある文脈を表現するためのもので、例えばリストから目的の要素を検索する場合などに役立つ。`type Option<A> = None | Some<A>` として、ある型`A`について`none`もしくは`A`を`Some`でラップしたものを返す。`none`ではなく`null` を、 値そのものではなく `some` で値をラップしたものを使うのは、`Option` という文脈を損なわないためである。

Scalaから持ち込まれたモナドで、HaskellではMaybeに相当する(はず)。Scalaのことは知らないものでスカラ...😰

```typescript
import { Option, some, none, option } from 'fp-ts/lib/Option'
import { array } from 'fp-ts/lib/Array'
import { sequence } from 'fp-ts/lib/Traversable'

export function getAllSomesOrNone<A>(xs: Array<Option<A>>): Option<Array<A>> {
  return sequence(option, array)(xs)
}

console.log(getAllSomesOrNone([some(1), some(2), some(3)])) // => some([1, 2, 3])
console.log(getAllSomesOrNone([some(1), none, some(3)])) // => none
```

このコードはリポジトリの[exercise(一問一答形式でfp-tsについて勉強していくもの)](https://github.com/gcanti/fp-ts/tree/master/exercises)に置いてある。このコードでは、`Array<Option<A>>` な配列を受け取って、配列に`none`が含まれていなければ `some` で各要素をラップした配列を、含まれていれば `none` を返す関数`getAllsomesOrNone`を定義している。

`getAllSomesOrNone` で気になるのは、fp-tsの`sequence`の実装であろう。

```typescript
// Traversable.ts より一部を抜粋

// applicativeはモナドcにくるまった関数a -> bを、c[a] -> c[b]な関数に変換する
export function traverse<F, T>(
  F: Applicative<F>,
  T: Traversable<T>
): <A, B>(ta: HKT<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, HKT<T, B>> {
  return T.traverse(F)
}

export interface Traversable<T> extends Functor<T>, Foldable<T> {
  readonly traverse: <F>(F: Applicative<F>) => <A, B>(ta: HKT<T, A>, f: (a: A) => HKT<F, B>) => HKT<F, HKT<T, B>>
}

// HKT: Higher Kinded Types(高階型)
// "型の型"を定義する
export function sequence<F, T>(
  f: Applicative<F>,
  t: Traversable<T>
): <A>(tfa: HKT<T, HKT<F, A>>) => HKT<F, HKT<T, A>> {
  return tfa => t.traverse(f)(tfa, fa => fa)
}

// URIは、Monadの識別子で単なる文字列("Option", "List"など)
export function sequence<F extends URIS, T extends URIS>(
  f: Applicative1<F>,
  t: Traversable1<T>
): <A>(tfa: Type<T, Type<F, A>>) => Type<F, Type<T, A>>
```

`sequence`は、 前述の文脈を持つ型であるアプリカティブ(`option`)と、走査して各要素に同じ関数を使うことができるような型(この表現はかなり乱暴ですが!)`traversable`(`array`)を引数に取り、関数を返す。その関数とは、二重の入れ子構造になっている高階型(`Option<A>[]`)を引数にとり、入れる順番を逆にした高階型(`Option<A[]>`)を返すものである。この例では`sequence`が返した関数に対して `Array<Opation<A>>` を渡しているため、最終的に`getSomeOrNone` からは `Option<Array<A>>` が返ってきている。目的の型を得られるわけである。

では、`sequence` はどのようにして`Applicative` と `Traversable` から `(tfa: HKT<T, HKT<F, A>>) => HKT<F, HKT<T, A>>` を得ているのだろうか？答えは`sequense` が呼び出している `Traversable.traverse` 関数にある。この関数は定義にある通り、いかにも部分適用して使ってくださいね:wink:というような型定義になっている。実際、この関数は`t.traverse(f)(tfa, fa => fa)` と呼び出されている。ここで、`f`は`Option`, `tfa` は `Array<Option<A>>`, `fa => fa` は `Option<A> => Option<A>` だと考えてほしい。

もちろん、これは`Option`以外のアプリカティブ(`Either`など)や`Array` 以外の`traversable`(`tuple`など)にも適用できる。型レベルでここまで高度なことができてしまうのである！

しかし…難しい！めちゃめちゃ難しい！頭が痛くなる！！でも慣れてくるといけそうな気がしてくる。(本当か？)何より便利なことは確かなので使えるようになったら嬉しいだろう。今書いているのはTypeScriptなのである。これでサーバサイドのコードもフロントエンドのコードも書けるのだ。

### Eitherモナド

Eitherは前述の通りで、失敗系と成功系を表現する型である。

```typescript
import { Either, left, right } from 'fp-ts/lib/Either'

export function elementAt<A>(xs: Array<A>, i: number): Either<string, A> {
  if (i < 0) {
    return left<string, A>('out of lower bound')
  }
  if (i >= xs.length) {
    return left<string, A>('out of upper bound')
  }
  return right<string, A>(xs[i])
}

console.log(elementAt([1, 2, 3], -1)) // => left('out of lower bound')
console.log(elementAt([1, 2, 3], 10)) // => left('out of upper bound')
console.log(elementAt([1, 2, 3], 1))  // => right(2)
```

これはもはや解説の必要すらないであろう。便利…！


### Stateモナド

最後に紹介しておきたいのがStateモナドである。
Haskellで書くこういうコードが、

```haskell
import Control.Monad.State
 
main :: IO ()
main = runStateT code [1..] >> return ()
--
-- layer an infinite list of uniques over the IO monad
--
 
code :: StateT [Integer] IO ()
code = do
    x <- pop
    io $ print x
    y <- pop
    io $ print y
    z <- pop
    io $ print z
    return ()
 
--
-- pop the next unique off the stack
--
pop :: StateT [Integer] IO Integer
pop = do
    (x:xs) <- get
    put xs
    return x
 
io :: IO a -> StateT [Integer] IO a
io = liftIO
```

fp-tsでこう書ける

```typescript
import * as stateT from 'fp-ts/lib/StateT'
import { io, IO } from 'fp-ts/lib/IO'
import { Monad2 } from 'fp-ts/lib/Monad'
import { Endomorphism } from 'fp-ts/lib/function'
import * as array from 'fp-ts/lib/Array'
import { State } from 'fp-ts/lib/State'

declare module 'fp-ts/lib/HKT' {
  interface URI2HKT2<L, A> {
    StateIO: StateIO<L, A>
  }
}

const stateTIO = stateT.getStateT(io)

export const URI = 'StateIO'

export type URI = typeof URI

export class StateIO<S, A> {
  readonly _A!: A
  readonly _L!: S
  readonly _URI!: URI
  constructor(readonly value: (s: S) => IO<[A, S]>) {}
  run(s: S): [A, S] {
    return this.value(s).run()
  }
  eval(s: S): A {
    return this.run(s)[0]
  }
  exec(s: S): S {
    return this.run(s)[1]
  }
  map<B>(f: (a: A) => B): StateIO<S, B> {
    return new StateIO(stateTIO.map(f, this.value))
  }
  ap<B>(fab: StateIO<S, (a: A) => B>): StateIO<S, B> {
    return new StateIO(stateTIO.ap(fab.value, this.value))
  }
  ap_<B, C>(this: StateIO<S, (b: B) => C>, fb: StateIO<S, B>): StateIO<S, C> {
    return fb.ap(this)
  }
  chain<B>(f: (a: A) => StateIO<S, B>): StateIO<S, B> {
    return new StateIO(stateTIO.chain(a => f(a).value, this.value))
  }
}

const map = <S, A, B>(fa: StateIO<S, A>, f: (a: A) => B): StateIO<S, B> => {
  return fa.map(f)
}

const of = <S, A>(a: A): StateIO<S, A> => {
  return new StateIO(stateTIO.of(a))
}

const ap = <S, A, B>(fab: StateIO<S, (a: A) => B>, fa: StateIO<S, A>): StateIO<S, B> => {
  return fa.ap(fab)
}

const chain = <S, A, B>(fa: StateIO<S, A>, f: (a: A) => StateIO<S, B>): StateIO<S, B> => {
  return fa.chain(f)
}

const stateTget = stateT.get(io)
export const get = <S>(): StateIO<S, S> => {
  return new StateIO(stateTget())
}

const stateTput = stateT.put(io)
export const put = <S>(s: S): StateIO<S, void> => {
  return new StateIO(stateTput(s))
}

const stateTmodify = stateT.modify(io)
export const modify = <S>(f: Endomorphism<S>): StateIO<S, void> => {
  return new StateIO(stateTmodify(f))
}

const stateTgets = stateT.gets(io)
export const gets = <S, A>(f: (s: S) => A): StateIO<S, A> => {
  return new StateIO(stateTgets(f))
}

const stateTliftF = stateT.liftF(io)
export const fromIO = <S, A>(fa: IO<A>): StateIO<S, A> => {
  return new StateIO(stateTliftF(fa))
}

const stateTfromState = stateT.fromState(io)
export const fromState = <S, A>(fa: State<S, A>): StateIO<S, A> => {
  return new StateIO(stateTfromState(fa))
}

export const stateIO: Monad2<URI> = {
  URI,
  map,
  of,
  ap,
  chain
}

//
// Usage (adapted from https://wiki.haskell.org/Simple_StateT_use)
//

import { log } from 'fp-ts/lib/Console'

/** pop the next unique off the stack */
const pop: StateIO<Array<number>, number> = get<Array<number>>().chain(ns =>
  array.foldL(ns, () => of(0), (h, t) => put(t).chain(() => of(h)))
)

const program1: StateIO<Array<number>, void> = pop
  .chain(x => fromIO(log(x)))
  .chain(() => pop)
  .chain(y => fromIO(log(y)))
  .chain(() => of(undefined))

program1.run([1, 2, 3])
```

コード量が多いので各実装の解説は控えるが、詰まるところは`StateT` を利用して `State` と `IO` の２つのモナドを組み合わせている。`of`, `ap`, `map` みたいなところも(HaskellやScalaに馴染みのない方にはわかりづらいのだが、Haskellのコードを見てわかるようにその手のプログラミング言語では自分で実装する必要はない)自分で定義しなければならないため、コード量がバカみたいに多いが、やりたいことは113~119行目の6行である。

## さいごに

もの凄くわかりづらい&ターゲットが不明な説明になってしまいました…ここ間違ってるよとかこう書いたほうがいいよとかそういうフィードバックはガシガシください。

というかそもそもfp-ts以前にHaskellめっちゃ難しくないですか…？すごHの次に読むべきおすすめチュートリアル/OSS情報を教えてください🙏