fp-ts 2.8.0 で追加された bind, bindTo について

e_ntyo

e_ntyo

Posted on August 23, 2020

fp-ts 2.8.0 で追加された bind, bindTo について

tl; dr

  • fp-ts-contrib の do notation に代わって、fp-ts 2.8.0 では Option, Either などの各種の Monad に bind, bindTo というメソッドが生えた。
    • CHANGELOG
    • fp-ts-contrib の依存なしに、do notation 的なコードがかけるようになった
  • do notation と同様に、bind, bindTo を使うことで複数の Monadic な値の計算でコールバックによりネストが深くなる問題を解決できる。

do notation

PureScript など、一部のプログラミング言語には do notation と呼ばれる記法があります。PureScript の公式ドキュメントからコード例を引用します。

maybeSum :: Maybe Number -> Maybe Number -> Maybe Number
maybeSum a b = do
  n <- a
  m <- b
  let result = n + m
  pure result
Enter fullscreen mode Exit fullscreen mode

maybeSumMaybe Number な値を 2 つ取って、和を求めて Maybe Number で返す関数です。do キーワードを使うと、その下のブロックでは例えば n <- a と書くことで Maybe NumberaNumber 型の変数として n に束縛できます。これの何が嬉しいかは、do notation を使わなかった場合の例を考えるとわかります。

maybeSum :: Maybe Number -> Maybe Number -> Maybe Number
maybeSum a b =
  bind a \n ->
    bind b \m ->
      let result = n + m
      in pure result
Enter fullscreen mode Exit fullscreen mode

bind a \n ->, bind b \m -> によりネストが二段深くなってしまいました。do notation を使った場合のコードとプログラムの内容が同じであるとすると、bindキーワードがどのようなはたらきをするかは直感的にわかるでしょう。今回の Post において重要なことは、「do notation を使うことで bind を使わずに済んでいる」、ということです(もしくはその逆)。PureScript の公式ドキュメントでは、bind x \a -> ...a <- x の糖衣構文であることが明記されています。

a <- x which desugars to bind x \a -> ...

fp-ts における do notation

話を PureScript から TypeScript というプログラミング言語に移します。TypeScript には標準で Monad や do notation のサポートがありません。しかし、 gcanti/fp-ts などのライブラリを利用することで Monadic なコードが書けるようになります。

ただ、do notation については gcanti/fp-ts-contribという別パッケージに切り出されていました。fp-ts-contrib が提供していた Do 、次のようにして使われていました。

import { Do } from "fp-ts-contrib/lib/Do";
import { option, some } from "fp-ts/lib/Option";

const result = Do(option)
  .bind("a", some(1))
  .bind("b", some(2))
  .return(({ a, b }) => a + b);
Enter fullscreen mode Exit fullscreen mode

ここからようやく本題に入っていくのですが、上記のコードが fp-ts 2.8.0 からは以下のように書けるようになりますよというのが今回共有したかった話です。

import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";

const result = pipe(
    O.bindTo("a")(O.some(1)),
    O.bind("b", () => O.some(2)),
    O.map(({ a, b }) => a + b)
  );
}
Enter fullscreen mode Exit fullscreen mode

Option などの各モナドごとに bind, bindTo が提供され、 fp-ts-contrib に依存せず do notation 的なコードが書けるようになりました。

O.bind("b", () => O.some(2))O.bindTo("b", O.some(2)) と書きたくなりますが、O.bindO.bindTo は引数の取り方が違う上、bindTo では pipe で流れてくる Option<A>A の情報を引き継ぐことができません。{ a: O.some(1) }を引き継ぐことができない)

上記のコードのようなケースでは、 bindTo もしくは bind を pipe の先頭で使い、以降は bind で繋いでいくという方針で書いていくと良さそうです。

おわりに

この記事はアルバイト先の株式会社 HERP のまざっちさん(@mazamachi)に勧められて書きました。また、まざっちさんには執筆の上でのアドバイスをいただきました。ありがとうございます 🙌

HERP では、TypeScript の高度な型システムや fp-ts などの関数型プログラミングのライブラリを駆使して、混沌とした Web フロントエンドの世界をともに生き抜いていくソフトウェアエンジニアを募集しております。オフィスは東京・京都・つくばにあってフルリモートも多分可能です。いい会社です!

💖 💪 🙅 🚩
e_ntyo
e_ntyo

Posted on August 23, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related