GWでHaskell入門した

日記です。 GWの自由研究でHaskell入門してた。

動機

一つに、同僚の影響がある。 弊社の社員は皆さまざまな言語を書く。 たまに「これはHaskellでいうと○○で〜〜」みたいな会話がされたりして、いつかはやらねばな〜と思っていた。 今年入ってきた後輩もHaskellとかOcamlをバリバリやっていたらしい。 今のチームではScalaを使っているので、関数型言語の概念とか考え方を一度ちゃんと学んだ方が有利に違いない。 入社から丸3年が経過した今、せめて同僚たちの会話をなんとなく理解できるようになりたい。

もう一つの理由は、ライブコーディング界隈でHaskellがよく使われていることだ。 現在ライブコーディング音楽で最も盛り上がっているTidalCyclesや、AltGLSLのHylogenなど、なぜかライブコーディング界隈ではHaskellが好まれている。 簡潔な文法や抽象的な操作が得意なことから、ガリガリ書いて実行していくライブコーディングに向いているのかもしれないが、まあ理由はどうでもいい。

TidalCyclesを使いこなしたりパッチを当てたりするには必須だし、Haskellを書けるようにならんとな〜と考えていた。

やったこと

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

まず、大学生の時買って積んでた「すごいHaskellたのしく学ぼう」(通称「すごいH本」or 「LYAHFGG」)をガッと読んだ。 Kindleには5年前に書いたメモとか残ってて便利。 仕事で1年間Scalaを書いたことで、学生時代よりもスッと理解できた感じがする。

すごいH本を読んで、色々な概念が登場することはわかったけど、まだ動くものを書ける気がしなかった。 というわけで、 Haskell チュートリアル とかでググって出てきた以下の教材をやってみることにした。

48時間でSchemeを書こう - Wikibooks

「48時間でSchemeを書こう」(以下「WYAS48」)は、簡単なSchemeの実行環境を作りつつ、Haskellの書き方を身につける、という教材。 そもそもSchemeの文法がわからない(大学でちょろっとやった)くらいなので、Gaucheで動作を確かめながら写経していった。

レポジトリはこちら。 「IOプリミティブの作成」の章までやってある。

github.com

ただ写経するだけじゃつまらないので、Stackでレポジトリを構成し、Hspecでテストを書いた。

Stackとは

StackはNode.jsでいうnpm、Scalaでいうsbtのようなもの。 ghcのバージョンを指定したりできるので、どちらかというとsbtに近い。 stack buildでビルド、stack testでテストが走る。

Haskellのパッケージマネージャーは、昔はCabalというものが使われていた。 HaskellのライブラリはHackageに公開され、 cabal コマンドでインストールできた。 ただし、Cabalは依存ライブラリを管理するファイルが独自フォーマットだったり、ライブラリの相性が悪いときに依存関係を管理するのが大変だったりした、らしい。

StackはCabalをラップして使いやすくしてくれる。 (正確には、StackはHpackというCabalラッパーを利用している)

StackにはStackageという仕組みがあって、これを使うとライブラリの相性を考える必要がなくなる。

Stackage は Stable Hackage の略で、どんな組み合わせでも依存関係でエラーが起きないように調整されたパッケージの集合 (スナップショット) を提供しています。

頭いいなー。 Stackageのバージョンは、stack.yamlresolver: lts-9.21 のように指定できる。

環境構築

まずstackをインストール。

$ brew uninstall --force cabal-install ghc # 紛らわしいので事前に消しておく
$ brew install stack

次にAtomHaskell関連のパッケージを入れる。 Atom-Haskellというプロジェクトがあって、Haskell用の便利パッケージがいくつも公開されている。 (コード補完、Linter、型チェック、コード変換、ドキュメント検索など)

atom-haskell パッケージを入れると、関連パッケージをまとめてインストールしてくれる。

atom-haskell.github.io

続いて、パッケージが依存しているライブラリをstackでインストールする。

$ stack install stylish-haskell
$ stack --resolver lts-9 install ghc-mod

ghc-mod は最新のStackageだと動かないので (ghc 8.2に対応していない)、 --resolver lts-9 を指定する必要がある。

最初、↑のWebサイトの存在に気付かず、自力でghc-modとかをインストールした所エラーが出まくってめっちゃ詰まった……

WYAS48学習メモ

  • ghc-modハマった
    • stack.yamlresolver: lts-9.21 としないとビルド通らない
    • dist/ があるとghc-modが動かないので消しとく
  • Control.Monad.Error 使ってるとこに Use Control.Monad.Except instead とメッセージ出るけど、こういうことっぽい
  • Hlintの声に従ってコードを整理していくと、どんどんコードの密度が高くなる……
    • liftM List $ load filenameList <$> load filename になる
  • maybe関数の引数の順序難しい
    • maybe (失敗した場合に返す値) (成功した場合に値を適用させる関数) (Maybeモナド) の順番
    • Scalafold に似てる?
  • モナド変換子、頭爆発するかと思った
  • doブロックで別のモナドを使うと怒られる
  • IORef使うと何故かHaskellの世界の外にグローバル変数みたいなのを作れる
  • テストでは unsafePerformIO 使ってIOモナドの中身を取り出してる
    • doブロックを抜けるとIORefで作った参照も壊れる?

感想

いろんな新しい概念を覚えるのは楽しいですね。 モナドとか型クラスとかは、Scalaでなんとなく存在を理解してたつもりだったけど、Haskellの方が実装が素直な気がする。 (Scalaの型クラス、ノールックで実装できない……クソザコ人材なので……)

すごいH本に出てきたZipperとかは、言語というより実装の知恵という感じで、デザインパターンを初めて学んだときと似た感じを受けた。 こういう先人の知識を組み合わせて問題を解くの、遊戯王カードやってるみたいな気持ちになる。

あとAtomでの開発環境構築はScalaの500倍楽だった。

やりたいこと

とりあえず夏のあいだにどれかやりたい:

  • CLIツールかなんか作る
  • Webアプリ作る
  • PureScript (Haskell風AltJS) で遊ぶ
  • TidalCyclesにPR送る

今年は他にもMUSTで学ばないといけない事があるので、目標は低め……。 (仕事でUnityとかTouchDesignerをやる可能性がある)

なんかオススメのタスクあったら教えてください。

参考URL