Haskell学習記(1)

少し前にHaskellの勉強を始めて軌道に乗りだしたので、初志を忘れないように、ポストLYHGGの歩みを書き留めておく。

きっかけ

  • 当時のプログラミング歴:C++ (< C++11), Python (numpy / (iter|func)tools / generators / coroutines)
  • Haskellを始めた動機:pythonクロージャーや高階関数、遅延評価のちからに目覚めたが、型チェック不在のpythonではデバッグやメンテナンスのコストが苦痛になってきた。「静的型付き関数型言語」を使えばこの悩みから開放されるらしいことを知る。その中でも原理主義の輝きに魅かれてHaskellを選択
  • 感想:中置演算子が多い、一行毎に含まれる要素が多い、読みにくい、しかしカッコいい

倦怠期:~半年

  • とりあえずLYHGGをよむ。予約語dataとかclassが馴染まず苦労する。やはりモナドあたりで消化不良を起こしながら読了。モナドにもいろいろ種類があることを知って動揺する
  • 試行錯誤。関数オーバーライドと継承がとてもやりにくい事に気が付き失望。C++11/14のラムダ式・関数オブジェクトに浮気するが、型多相性やモジュラリティが物足りなく感じる
  • C++autoを使ってみて初めてHM型推論の素晴らしさに気がつく
当時Haskellに抱いていたおぼろげなイメージ
  • 全てが不変値だから、数学的な思考と馴染みやすい
  • 求めたい値を宣言的に表現すると、コンパイラが効率の良いアルゴリズムを吐いてくれる(※違う)
  • 宣言的であるがゆえに、手続き的なパラダイムと親しい「アルゴリズム」や「データ構造」と親和しなそう

Haskell再び:~1年

仕事用のコードをHaskellで書く、という何度目かのチャレンジ中に、ずっと抱いていた違和感の根っこに気がつく。 それは

Typeclassは型のインターフェースというより関数のインターフェース

ということ。 ある型に作用する関数の集まりから共通の構造を括りだしたものがtypeclassであり、typeclassの主役はそれら関数(e.g. Numクラスの (+), (-), (*), ..)だった。実際、言語拡張無しには同名の関数を異なるtypeclassに所属させることはできない(!)が、同一の型を異なるtypeclassのインスタンスとする事は可能(当然)だ。 これが個人的に思うOOPHaskellの一番の差で、気づいてからモヤモヤがだいぶ晴れた。 この発見を期にHaskellの学習が勢いづき、脱初心者を目指すことに。 その試みの中で特に効果のあったと思う事柄は:

  • atomエディタ + ide-haskell導入
  • WIWINWLHを読む。Typed hole便利
  • ghcのドキュメントを読む。特に言語拡張について。NewTypeDeriving拡張は継承に便利
  • 圏論Haskellのつながりを整理し、モナドを理解する。モナドは結局typeclassの一種でしかない。ただそれがあまりにも汎用的かつ強力なtypeclassなので、理解が及ばないことに怯えていただけ。普通に使っていればモナドインスタンス宣言を書くことはなさそう
  • 標準ライブラリのソート関数やordered mapの実装を読み、Haskellアルゴリズム/データ構造の交わりを体得。関数型言語とはいえ吐かれるバイナリは手続き的だ。つまりHaskellは関数を用いて手続きを記述する言語だ。このきつい縛りと引き換えに、強力な抽象化の恩恵を受けられる

これから

準標準っぽいライブラリに手を出していきたい。Lensとか。