どうやってPureScriptに慣れたのか

はじめに

PureScript Advent Calendar 2017 - Qiitaの1日目の記事が埋まっていなかったので大急ぎで書くことにしました。

元々はRubyJavaScriptなどの動的言語をメインでメシを食べているプログラミング的ゆとり世代であった私が、 少なくとも一般論としては学習が大変だと評されているPureScriptを、 (上手いか下手かは別として)普通に書けるようになった要因を記憶の限り辿ってみようと思います。

つまり、この記事は、「最初からPureScriptの勉強のためにやっていた」ということではなく、「たまたまそういう道を通ったけど、 あれがよかったのかもしれない」という回想です。

道のはじまり

この道のはじまりはHaskellでした。

特に仕事で使うわけでもなく、関数型言語に興味があったわけでもなく、ただなんとなく暇を持て余していたので暇つぶしとして思いついて勉強し始めました。どうやって勉強していたかというと、いわゆるすごいH本をダラダラ読みながら適宜写経するという方法でした。 この本は、基本的な構文や畳み込み、型クラス、IO、ファンクター、アプリカティブファンクター、いくつかの基本的なモナドなどについて、ゆとりでもわかりやすく書かれていました。どういうものなのか?なんのためにあるのか?ということがある程度はわかった気になれたと思います。

ちなみにこの時点では、基本的に知識の定着はしないですが、雰囲気だけは忘れずに頭に残っている状態になります。 本による勉強ってそんなもんだと思っているので、あまり真剣になりすぎず流し読みするのが良いでしょう。

また、アプリカティブファンクターについては、実用時の想像が頭になく、いったいなんなんだこれは?という感覚をもっていましたが、 Applicativeのススメ - あどけない話を読んだらそれは解消されました。 メリットやアプリカティブスタイルであるときとないときのコードの違いが書かれていて腑に落ちました。

モナドについては、モナドの力がいまいちわかりませんでしたが、

Stateモナドがわかればモナドがわかる - セカイノカタチモナドってなんだよ!?全然わからないんで分解して図解してみた(´・ω・`) - セカイノカタチを読んだことによって、理解が進んだように思います。

実際に書きはじめる

Spock

とりあえずフレームワーク使って書いてみようということで、フレームワークを調べました。 初心者の悲しい性ですが、シンプルで素朴な感じのフレームワークはどれだろうか?という視点で調べていてヒットしたのがSpockでした。

はじめたものの、SpockM conn sess st ()←この型を見ても意味不明で、とりあえず理解することを一旦諦めました。 当然意味がなにもわかっておらず、コンパイラに怒られまくっていたのですが、エラーの内容を見て直し続けると、 どういうわけか動いてくれるという不思議な体験をしました。 仕事で静的型付け言語を書いたこともあり、コンパイルエラーのときは常にいらついていた私でしたが、この時に「もしかしてコンパイラって友達なの?」という感覚が芽生えたのは新鮮でした。「俺たちは雰囲気でプログラミングをやっている」という感覚でした。 ここで身についたのは知識というよりも「コンパイラに身を委ねる感覚」だったかなと思います。

Servant

知り合いのHaskellerに「フレームワークは何がいいのか?」と聞いたら、servant – A Type-Level Web DSL — servant documentationだ、ということなので使い始めました。 「型でルーティングするとかまじかっけーな」という小学生並みの感想だったのですが、このフレームワークは私にとってはかなり勉強になりました。

これはドキュメントがお宝だらけだったからだと感じています。例えば、チュートリアルの最初に言語拡張の宣言が書いてあったりするのですが、なんとなく学習していたらお目にかかれないものが山ほど書かれていて、1個1個調べるのは大変でしたが、かなり勉強になりました。

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

私はこのServantを土台に、認証、DB操作やプーリング、JSONのレスポンス生成などを試すことによって、上記の言語拡張に加えて、各種モナドトランスフォーマーや自動導出、自然変換など、色々なことを知ることができました。

当初は、とにかくコンパイル通すのに四苦八苦していましたが、 コンパイルエラーのメッセージが尋常じゃないくらい親切だった記憶があり、 コンパイルエラーと戦っているだけで訓練されていっている感覚がありました。 この時は、何かを調べようとすると何かを調べないといけない、みたいな感じになることも多く、一番勉強になった時期だったかと思います。 私はServantで普通のAPIサーバーを書くのはかなり効率のいい学習方法だったように感じています。

Elm

上記のようにHaskellを勉強したのち、フロントエンドも関数型で書いてみたかったのでElmを書き始めました。 Haskellを学習した後にやったためか、頭おかしいくらい学習コスト低いと感じてびっくりしたのを覚えています。

掲示板をつくりかけてみたり、自分のgithubページをElmで書き換えて見たり、Haskellとあわせて個人サービスをつくりかけてみたり、全部途中でやめる程度ではありますが、認証、通信、port、デコード、タイマー、各種イベント、それなりに色々できた感じがします。

Elmは型クラスないので、当然モナドもないのですが、書いていると「あ〜このへんがこうなってるのは副作用が伴う処理だからこんな感じになってるんだよな」みたいな感じのことを考えながら、フロントエンドという分野で、「何にどう型付けしているのか」が結構理解できました。

PureScript

ElmのあとにPureScriptを始めました。やっとPureScriptの話ができます。

と言いたいところですが、実はこのセクションについては書くことがほとんどありません。 もうすでにこの時点で、言語そのものが理由で書くのにつまるということがほとんどなくなっていました。(Effだけちょっとハマったけど)

一応Read PureScript by Example | Leanpubは読みましたが、ほぼ流し読みで、一生懸命読んだのはEffの章とFFIの章くらいです。

話は変わりますが、私はPureScriptをはじめてから型レベル計算を真面目に勉強したのですが、GitHub - paf31/purescript-quickserve: Quick HTTP serversが入門としてかなりわかりやすかったです。 各種エンドポイントでRecordをつくり、そのRecordでルーティングするところを読むと結構理解が進みます。

全体を通してやっていたこと

基本的に移動中は、使おうとしているパッケージを読むということをしていました。大体最初はほとんど読めないし、だるくなって読むのやめたりするのですが、そういうのはあまり気にしません。時間が解決してくれます。必要なのはコードの実態を目で見ることだけで、それさえしておけば、書いているうちに理解が後追いで一気に来ると思います。

最後に

急がば回れではないですが、HaskellとElmを先にやったのは結構よかったんじゃないかと思います。 Haskellは非常に情報量が多く調べやすいこと、そして、PureScriptよりも親切なエラーメッセージがあり、 調査に困らないし、コンパイラが勝手に訓練してくれます。ビシバシと鞭でケツを叩かれている感じで、ンギモヂイイイイイイイイイイイイイイイイイイという感じでしょうか。おそらく、PureScript書く上で、この時の体験が一番大きいです。

また、Elmに関しては、学習コストが低いため、ブラウザという戦場ではどこでどのように型付けしているのか、という世界観をつかむことに集中できます。

それと、学習する時に、いかに適当に雑に頭に入れておくかってことととりあえず書くということをしていれば、とりあえず書けるようになるぶんには、はじめてプログラミングを勉強した時と比べて学習コストが特段高いと感じない気はします。

PureScript興味あるという人は、Haskell と Elmから勉強すると実は近道かもしれません(適当)