Edited at

関数型プログラミングのお勉強リンク

More than 3 years have passed since last update.


はじめに

これは自分用の関数型プログラミングお勉強ノートです。

Atom や CoffeeScript に少し退屈してきたので別のお勉強をすることにした。

関数型プログラミングを学ぶことにした。

時々、Qiita の記事とかは読んでいたが、ガーッと調べてやりだしたのは半月程前の 2016年の6月の初旬から。

しばらく続けてみようと思う。

調べ物がどんどん拡散して行くので整理の意味でここに dump しておく。

自分が使う用。

自分が読んで良かった or 良さそう、なリンクに絞ってある。

リンク集としては既にとても豊富で、簡単に消化し尽くせないので自分用としてはこれで十分だ。

ちゃんと消化していくには相応の時間がかかるだろう。

同じように関数型プログラミングを初歩から学ぼうとしている人の参考になるかもしない。

俺のプログラミング勉強法は、これまでの経験上、以下の様なパターンがある。

今は 1. の座学の時期。


  1. ガーっと調べて情報をひたすら詰め込む(座学)

  2. 1で得た知識によりコード読む(分からなかったら動かす。print デバッグしてみる。それでも分からなかったら座学に戻る)

  3. 部分的に書き換えてみる。変更に慣れる。まるごと書き換えてみる or 同じものを作ってみる

  4. 自分でツールを作ってみる。


座学の今、どうやっているか?

ひたすら、読み物を読んでいる。時々 REPL やファイル実行で試すが、今のところ座学の方が多い。


分からないものを読んでる時は頭がつかれてすぐ眠くなっちゃう。


特にどの言語、というのを決めず、ひたすら理解できそうなものをつまみ食いしている。


当たり前すぎて説明されていないのであろう、基本的な事を、そもそも分かっていない為か、いろんな所が分からず引っかかる。


詰まると他の読み物に脱線して、理解を少し補った上で分からなかったところに再チャレンジ、みたいな感じで続けている。


結果、様々な関数型言語に拡散しつつ勉強を続けている。


対象言語

特に対象を決めているわけではないが、触れた言語は以下。


Elm

An Introduction to Elm

A guide that covers all the major aspects of Elm. — Evan Czaplicki

Elm 作者(evancz)が書いている Gitbook.

はじめは Elem Architecture の部分は飛ばして、Types のところから読む方が良い。

勉強を始めた頃、Elm に限らず、関数型言語全般の関数の Type Annotations が読めなかった、読めても納得感が無かった。


とてもおすすめ。どの言語でも関数の型を自分なりに読めるようになった。


他にも、Union Types とか諸々の説明が素晴らしい。Elem Architecture は実践的すぎて????となる所が超沢山あるが、Types 以降は超おすすめ。

Evan さんに Haskell の説明も書いてほしいわ。。


脱線: 関数型言語の関数の型(Type Annotations)をどうやって読むか?

例えば以下の様な2つの引数を取る関数がある

divide : Float -> Float -> Float

divide x y =
x / y

なぜ divide : Float -> Float -> Float なの?


divide : (Float, Float) -> Float とかの方が分かりやすいのに(CoffeeScript脳)。


みたいな引っ掛かりがあった。これはカリー化という技が関係している。


複数引数を取る関数は、1引数を取る、関数を返す関数で表現でき、引数を一つづつ渡して、返ってきた関数に次の引数をまた渡して、、、とやっていけば同じ結果になる。


だから、複数の引数を取る関数は、一つ引数を取り、"次の引数を取る関数"を返す関数として表現可能、、これをカリー化、と言うらしい。

以下の様に変換出来る。

'2引数(a, b)を取る関数' = (('1引数(a)を取る関数' → return '1引数(b)を取る関数')(a))(b)

1引数(b)を取る関数は outer variable としての a にアクセス可能なので(= closures)、今回渡される b と、アクセス可能な a を使って計算ができる、という訳。


一気に複数引数を受け取らなくても、クロージャ使えば、順番に1つずつ受け取っても同じ事が出来るという事。

CoffeeScript で書くとこうなる

# 2引数を取る関数を

# -------------------------
concatTwoString = (s1, s2) -> s1 + s2
# 普通に実行
concatTwoString("a", "b") # => ab

# 1つの引数をとる関数のみを使って同じことをやってみると
concatTwoStringCurry = (s1) ->
(s2) ->
s1 + s2

# まずひとつ引数を渡し、concatWithA に返ってくる関数を代入(bind)
concatWithA = concatTwoStringCurry("a")
# 次の引数を渡すと、、同じことが出来る
concatWithA("b") # => "ab"
# 中間的な変数 concatWithA を使わなくても同じ事ができる。
concatTwoStringCurry("a")("b") # => "ab"

# 3引数を取る関数でもやってみる
# -------------------------
concatThreeString = (s1, s2, s3) -> s1 + s2 + s3
concatThreeString("a", "b", "c") # => abc

concatThreeStringCurry = (s1) ->
(s2) -> # 1つめの引数で返す関数
(s3) -> # 2つめの引数で返す関数
s1 + s2 + s3 # 3つ目で最終結果を返す

concatThreeStringCurry("a")("b")("c") # => abc

Elm に限らず、purescript, Haskell, OCaml, Reason の全てで、自動的にカリー化が行われる。以下の特徴がある。


  • 引数を一度に渡さなくても良い(=これを partial application, 部分適用というらしい). 3つの引数をそれぞれ別々のタイミングで渡したり出来る。

  • 引数は1つずつ渡され、関数へ apply(function application)される

  • Function application は左結合(left associative)、なので、括弧は不要。(= f 関数に a, b, c を渡す場合 f a b c と書いた時 ((f a) b) c と解釈される。つまり括弧省いて f a b c と書いてもいい感じに順番に一つずつ引数渡して返ってきた関数に順番に次の引数渡してくれる様に結合ルール、強度が設定されている。

これを理解すると、divide : Float -> Float -> Float に納得がいく。


色々な読み方

divide : Float -> Float -> Float の場合

初心者向けの良くある説明


  • divide は2つの Float を受け取って、Float を返す関数

    これはカリー化の部分をすっ飛ばしており、型アノテーションの表記(なんで ->が2回出てくるんだ?)がしっくり来なかった。

もう一つの読み方(->を then の様に読む)


  • devide は Float を受け取って、次(->)にFloat を受け取って、最後(->)にFloat を返す関数

英語の様に左から右の語順で読もうとしてみる.


  • devide は Float を受け取って、関数を返す(Float -> Float)、どんな関数?それは Float を受け取って、Float を返す関数

この作戦で Map を読んでみる

map :: (a -> b) -> [a] -> [b]


  • map は、 a を受け取って型 b を返す関数((a -> b)) を受け取り、関数([a] -> [b]) を返す。返ってきた関数はどんな関数? 型 a のリスト [a] を受け取って型 b のリスト [b] を返す関数。


重要なのは複数の引数は、1つずつ渡され、返り値として返ってきた関数にまた次の引数が渡されるという、順番に渡される イメージ。

例えば、3引数の関数だと、渡す(1つ目) -> 関数返ってくる -> 渡す(2つ目) -> 関数返ってくる -> 渡す(3つ目) -> 最終結果が返ってくる といった流れ。

関数というのは変換(transform)のイメージなので、関数をTransformer と書き換えてみる(正しいかどうかは知らんが自分の納得感がある)

map は 型 a から 型 b への transformer(=(a -> b)) を受け取り [a] から(->) [b] への transformer(=[a] -> [b]) を返す。

この返ってきた Transformer(=関数)に [a] を渡すと [b] が返ってくる。 変換ルール(transformation rule)は第一引数に渡した transformer で指示した通り。


Haskell

Haskell を学ぶのも教えるのも、色々な情報があり模索がされているよう。

うまく教えるのが難しい言語?


Christopher Allen

Haskell を教えることにすごく情熱があり、実際に何年も教えている Christopher Allen という人がいる。

数学の理論的背景を元にした説明はある分野の人にとっては分かりやすいが、その文脈を共有していない人からすると、怖がらせ、敬遠させる悪い効果しかない。Haskell は難しくないが、教える人と、教わる人の文脈(背景となる共通用語)が合っていない場合、実際以上に難しい印象を与えてしまっている。というような事を Changelog ポッドキャストで言っていたような。。

github | Blog | twitter


learnhaskell

Haskell を学ぶおすすめ path が書かれている。Readme 以外にも色々有用なドキュメントがある。

"DO NOT INSTALL HASKELL PLATFORM" とか書いてある。

まず最初に、"Yorgey's cis194 course" をやれとある。

http://www.seas.upenn.edu/~cis194/fall14/spring13/

これはペンシルバニア大学の2013, Spring の講義なのでこれより新しい物が各年分あるのだが、

あえてこれを推奨している理由があるのだろう。この時の講師が Brent Yorgey さん.


Haskell is easy

ここでも Install には Stack 使えって書いている。


Haskellbook

Changelog のエピソードで知った。この本は良さそう。高いけど買った。まだ読み始め。

Julie Moronuki さんとの共著。Julie は言語学者。全くのプログラミング初心者で、最初に覚えた言語が Haskell。Chris が twitter で 「Haskell 学ぼうぜ」と説得し、教え始め、その数年後(?)に本を書き始めた。実際に全くのプログラミング初心者にHaskell を教えた経験がこの本に生きている(らしい).

既存の Haskell 本は別のプログラミング言語の経験を前提にしており、analogy(例え、類推) がうまく働くこともあれば、逆に不正確な analogy が学ぶことの障壁になる、という場合もあった。全く白紙から Haskell を教えるというアプローチがこの本の特色。


Brent Yorgey

Chris が推奨している cs194 のコースの講義をしていた人


  • Blog


  • ペンシルバニア大学の2013, Springの講義: Christopher Allen がおすすめしているもの


  • Introduction To Haskell: cis194の講義が元ネタのチュートリアル。講義はまだやってないが、こちらは全部やった。とんでもなく素晴らしい。簡潔、明瞭かつ、深い。色々曖昧な点がクリアになっていく。超説明上手。例が適切。講義で練られているからもあるだろう。とてもオススメ。


Official

documentation は有用リンクの宝庫

ここでも CIS194 のリンクがあるが、chris が推奨している Brent Yorgey のものではないので注意


Commercial Haskell SIG



  • commercialhaskell: github の Organization


  • commercialhaskell の説明
    多くの企業、個人が参加している。Haskell の商用利用を促進する上での情報共有、改善の取り組みみたいな。


  • stack: stack も彼らがメンテしている様


  • jump
    Haskell でアプリケーションを書き始める時の Jump Start を助ける。Opinionated に推奨パッケージを記載している。Haskell Platform とゴールは重なっているけど?違いは?みたいな点についても書かれている。有用リンクも沢山。必読.


その他


purescript


  • Getting Started with PureScript

    最初にこれをやるべし


  • PureScript by Example

    purescript の作者によるもの。最新に追随してないので、そのままだとサンプルが動かん。色々と動かすだけで引っかかりすぎるので、とりあえず読み物として使っている。


  • 実例によるPureScript

    PureScript by Example の日本語訳、英語の最新版には追随していないので大きく差異があり、そのままだと英語版よりもっとサンプルが動かん。

    訳者は @hiruberuto さん。 訳語が注意深く選ばれているので、原文の英語をどう捉えるか?と迷った時に該当箇所の訳文を読むと理解の助けになるという使い方をしている。

    訳者自身による説明も以下にある。

    http://qiita.com/hiruberuto/items/f1e048fc9a8ca51eddb2



OCaml

Objective Caml 入門

京都大学の五十嵐 淳さん(当時?)による講義のノート。説明が順を追っていて(理由のすっ飛ばしがない)、簡潔で素晴らしい。


Reason

Reason

React のオリジナル Author である jordwalke 氏がやっている。

OCaml の Syntax をより Accessible にしたもの。バックエンドは OCaml の ToolChain まんま使っているよう。

俺自身はのざっくりとした理解は、Reason(=CoffeeScript) -> OCaml(=JavaScript)。

HOW REASON WORKS にどういうものが書かれている。

多くの OCaml プログラマがハマりやすい Syntax を修正(Context によって同じものを表現するのに別の Syntax が必要なので覚え難いみたいなのを修正して、より統一的に書ける様にするとか)している。


  • REASON AND OCAML

    OCaml プログラマ向け

    OCaml で混乱した時、こういうのを読むとハマリポイントがまとめて知れてお得。

    ちょうど CoffeeScript のモチベーションを知ることで JavaScript の Bad parts(ハマリポイント集)をまとめて知れてお得なのと同じ。

    例えば CoffeeScript の Introduction から 以下の様な事を学ぶ事ができる。

    例えば、var 無しの変数宣言を禁じている、→ var 無しの変数が global 名前空間を汚染するというハマりポイントを知る。

    do (x) => 記法が loop の中で変わる変数 x を外部の変化から守るために、使えるとかから → 変数のブロックスコープは無く、関数スコープのみ。みたいな事を知る。

    それと同じで、Reason と OCaml の対比で OCaml の Bad parts を知ることが出来る。


  • REASON AND JAVASCRIPT

    Reason resembles a typed subset of modern JavaScript (the good parts)

    自分なりに意訳すると

    Reason は今風 JavaScript良い所を抽出し、型付き版にした言語に似ている



関数型プログラミング全般

Qiita で以下の人をフォローし、記事を全部読む。コードサンプル記事で分からんものはとりあえず飛ばしている。

@esumii : 住井 英二郎 さん、大学の先生.

@hiruberuto : 謎の人。用語がとても意識的に選ばれていて、すごく文章力あり素晴らしい。自問自答して回答をする形式も多く、想定される疑問に正面から答えていて読むだけでとても勉強になる(教育的)。もっと記事を書いて欲しい!

@nobsun : 山下伸夫さん。沢山 Haskell 本を翻訳、監修してる人