こんにちは、LINEでフロントエンド開発を担当しているJunです。 2016年10月24日~28日の5日間、「LINE Haskellブートキャンプ」というプログラムがLINEの渋谷オフィスにて開催されました。今回の記事では、同プログラムに参加した感想を書きたいと思います。 Haskellとは Haskellは、柔軟性、合成可能性(composability)、安全性を維持しつつ、高性能のソフトウェアの作成を可能にする現代的なアプリケーションプログラミング言語です。ここ最近、Facebookやスタンダードチャータードなど複数の企業でHaskellを導入して商用利用の可能性を検証した例が増えており、数多くの言語・ライブラリがHaskellの方法論を借用して業界から注目を集めています。 LINEでも勉強会を立ち上げてHaskellを学習したり、社内サービスをHaskellで開発したりするなど、Haskellへの関心が高まっています。こうした中、5日間にわたって開かれた「LINE Haskellブートキャンプ」は、Haskellに興味のあるエンジニア同士が集まってHaskellに入門してみるプログラムでした。同ブートキャンプは、Haskellに対する奥深い学術的考察というよりは、誰もがHaskellに興味を持って使い始められるように、初心者レベルから学習することを目標にしました。進行役は、Haskellを使って社内サービスを開発した経験があるHanさんが担当してくれました。 なぜ、Haskellなのか Haskellで高性能のプログラムを安全かつシンプルに作成する例を紹介します。 import Data.List (isPrefixOf) appendIfNeeded xs ys = if xs `isPrefixOf` ys then ys else xs ++ ys 上記のappendIfNeeded関数の定義にはデータ型を一切明示していません。でも、Haskellの型推論のおかげで、関数の引数であるxsとysは比較できるリストであることが保証されます。この関数にリストではない値を入力した場合、またはリストの引数が比較できないデータ型である場合は、コンパイラはエラーを出します。Haskellの型推論は、コードをシンプルに維持しつつ、間違ったコードが実行されるリスクを減らします。 Haskellが保証する安全性は、値の型だけにとどまるものではありません。以下のような型を持つ関数があると仮定してみましょう。 toString :: Integer -> String 上記のtoString関数は、整数を引数として受け取り、文字列を返す一般的な関数で、どの言語でも作成できます。しかしHaskellは、この関数について、「同じ整数が引数として提供されれば、いつ、どこでこの関数が実行されても同じ文字列を返す」ということを保証します。つまり、Haskellの関数は、同じ入力値に対して常に同じ出力値を返すことが保証されます。一般の言語では、ある関数を呼び出したときにどんなことが起きるかは、コードを確認しない限り分かりません。ある関数を呼び出すことで、意図せずにファイルシステムにある特定のファイルをサーバに送信したり、大陸間弾道ミサイルを発射したりすることもあり得ます。しかしHaskellでは、関数の型を見るだけでも、そうした期待外れの動作が発生しないことを確信できます。 readFile :: FilePath -> IO ByteString 一方、readFileは、特定のファイルパスを受け取り、そのファイルの内容を読み込む関数です。この関数はファイルシステムにアクセスする必要があり、ファイルシステムはいつでも変化できます。Haskellはこの場合、IOという型で関数をアクションにすることができます。アクションは、実世界とコミュニケーションすることができ、ファイルの入出力やネットワーク通信などの有意味な副作用を作り出すことができます。大事なのは、IO型によって指定されたアクションだけがそのような副作用を作り出せるということです。Haskellでは、副作用に対する例外処理のためにアクション以外のコードを確認する必要がありません。こうした副作用の分離は、コードの構造を改善するだけでなく、メンテナンスにかかる手間も減らす効果があります。 このように、Haskellは型レベルで副作用を管理することで参照透過性(referential transparency)を確保できる言語です。どの関数がどの型の値を引数として受け取って返すのか、時間と空間に関係なく常に同じ値を返すのか、副作用を発生させることができるのか、などをコンパイル時に把握できます。その結果、同時性と並列性が実現しやすくなります。同時性と並列性は、現代のプログラミングにおいて重要なテーマであり、かつ難しいテーマでもあります。ほとんどの言語では、プログラマがより注意を払うことでそのような難しさを克服します。それに対し、Haskellコンパイラは十分な情報を持っているため、独自で同時性を管理できます。 f x + g y 上記は、fという関数をxに適用した結果と、gという関数をyに適用した結果を合計する式です。Haskellの「+」はIOアクションではないので、コンパイラは型を明示しなくてもfとgもアクションではないことが分かります。これは、f xとg yが時間と空間に関係なく同じ値を返すという意味です。そのため、f xとg yが同時に実行されても、他のCPUコアで実行されても結果には影響を与えません。したがって、コンパイラに並列化に関する簡単なヒントを与えるだけでも並列計算を実行するプログラムを作ることができます。 f x `par` g y `pseq` f x + g y 上記のコードにおいて、par関数はf xとg yを同時に実行させ、pseq関数は左側の実行が完了してから右側の計算を実行させます。ここで適切なコンパイル引数または実行引数を指定すれば、f xとg yが自動的に並列化します。同時性・並列性の実現のためにプロセスやスレッド、スレッドプールなどを使いながら奮闘する必要はありません。 以上のように、Haskellはシンプルなコードで安全性、参照透過性、同時性などを確保できる合理的かつ革新的な設計となっており、性能や合成可能性、コミュニティ、エコシステムなど、様々な魅力を持っています。 Haskellブートキャンプ ブートキャンプは、月曜日から金曜日までの5日間、午後に2時間ずつ毎日行われました。相当な時間を投入しないといけないプログラムでしたが、たくさんの開発者が集まってくれました。会場がいっぱいになるほどの人数で、Haskellに対するLINEエンジニアたちの熱い関心をうかがうことができました。 同ブートキャンプは、まずHanさんのスライドを見ながらHaskellの基本概念と規則を学習し、その後みんなでライブコーディングを実習する形で行われました。Haskellの文法はシンプルですが、慣れていない初心者には少し難しいかも知れません。実際にHaskellを学ぼうとする人の多くが、この段階でよく諦めてしまいます。LINE Haskellブートキャンプは、Haskellに入門する開発者たちがHaskellのコーディングに慣れることに重点を置きました。 興味深いことに、Haskellブートキャンプに参加した開発者たちの主な担当分野は、クライアント、サーバ、フロントエンドなど多岐に渡っていました。Haskellは、「なんとしても成功を避けよう(Avoid success at all cost)」という非公式のモットーを掲げ、型破りで実験的なプログラミング言語の発展を図ってきました。こうしたHaskellの発展は、他の言語にも良い刺激剤となりました。Javaのラムダとジェネリクス、ScalaのOptionやFuture型、SwiftのOptional chaining、JavaScriptのPromiseやasync/awaitなど、多数の言語の機能がHaskellの影響を受けて誕生しました。これはつまり、Haskellを学習すれば、普段使っている言語への理解も深まることを意味します。Hanさんは、Haskellの概念を説明する際に、それに対応する他の言語の機能も合わせて紹介してくれました。そのおかげで、Haskellの理解を促進することができ、プログラミング分野におけるHaskellの価値を実感することもできました。 ブートキャンプでよかった点は、果てしなく難しく感じられるかもしれないHaskellを、実用の観点から説明しようとしたことでした。例えば、Haskellというと通常、ラムダ計算、圏論、型クラス、モナドといった、聞くだけで難しくて怖いイメージの概念が思い浮かびます。しかしHanさんは、柔軟性、合成可能性、安全性といった実用面にフォーカスしてHaskellを説明しました。当たり前のことですが、Haskellを使うために圏論の博士号などが必要なわけではありません。Haskellを学習する方法も他のプログラミング言語のそれと大して変わりません。まず触れるレベルになったら、いろいろ作ってみながら少しずつ難しい概念を習得していく。LINE Haskellブートキャンプはその第一歩として重要な役割をしたと思います。 LINE Haskellブートキャンプは、通常のHaskell勉強会とは若干異なるカリキュラムを採用しました。一日目は、Haskellコミュニティで一般的に使われるプロジェクト管理ツールであるStack、パッケージおよびドキュメントのリポジトリとして使われるStackageについての説明がありました。さらに、String型の非効率を解決するTextやByteString、Haskellのメタプログラミングを可能にする拡張のTemplate Haskell、Webアプリケーション・インターフェースであるWAIなど、今すぐHaskellを使って何か面白いものを作るために必要なツールを紹介しました。Haskell勉強会は下手すると学術的になりがちですが、LINE Haskellブートキャンプは多様な分野の開発者に役立つ実用的な内容で構成されていたことが印象的でした。 もちろん、5日間ずっとコードとライブラリの話ばかりしていたわけではありません。ブートキャンプの重要な目的の一つは、Haskellへの興味と意欲を引き出すことでした。そのため、HanさんはHaskellを使うべき理由について様々な例を交えて説明しました。LINEでは、数多くのエンジニアたちが、それよりさらに多くの装置を用いて、それより遥かに多くのユーザーが利用するサービスを開発しています。このような環境で一番重要なことの一つは、サービスの信頼性(reliability)です。他の多数の言語が信頼性を確保するために様々なツールと規約を開発者に強制しているのに対し、Haskellはプログラミング言語レベルでより安全なプログラムを保証します。信頼性が何より重要な状況なら、Haskellはいい選択肢になるはずです。また、Haskellは、プログラミング自体に興味と情熱を持っている人々から大きな注目を浴びています。これはつまり、Haskellを使用すれば、強いモチベーションを持っている優秀なエンジニアたちと一緒に働ける機会が増えることを意味します。LINEのように優秀な開発者の募集に積極的な会社であるほど、Haskellの価値はさらに高く評価されるはずです。 最終日の「モナド怪談」セッションもとても興味深い内容でした。モナドはHaskellを難しく感じさせ、Haskellを学ぼうとする人々を挫折させる概念の一つです。実際に、モナドという概念は圏論からきたもので、これを完璧に理解するには高度な学術的知識が求められます。Hanさんはこれに対し、「モナドを理解することでHaskellプログラマになろうとするのは、『楽器とは何か』を理解することで楽器の演奏者になろうとするのと同じ」という表現を引用しました。楽器の演奏者になるために楽器の哲学的な意味を理解する必要がないように、Haskellを実用ツールとして使用するためにモナドの学問的な意味まで理解する必要はありません。結局、圏論とモナドという単語から感じられる怖さ自体が、Haskellの学習で陥りやすい落とし穴なのです。「モナド怪談」セッションでは、そのような落とし穴の存在をHaskell学習者に事前に知らせ、難しい単語のせいでHaskellへの興味を失ってはいけないと強調しました。 おわりに Haskellに興味のある方は、LINEでHaskellを使用していることや勉強会が開催されたことに驚いたのではないでしょうか。私もかなり前からHaskellに興味を持っていましたが、それを会社で使う機会があるとは思いませんでした。LINE Haskellブートキャンプは、LINEがIT企業として技術とツールの重要性を忘れない会社であり、向上心に満ちた仲間たちと一緒に働ける職場であることを改めて実感できるきっかけとなりました。個人的にもわくわくする楽しいプログラムでした。 同ブートキャンプで得られた最も大きな成果は、Haskellが商用利用に必要な実用性を十分に備えていることを確認できたことでした。Haskellが学界発の言語であることは確かですが、すでに世界有数の企業がHaskellを導入して成功を収めています。これは、Haskellが学界だけでなく、産業界でも有用なツールであることを裏付ける証拠であると、Hanさんは強調しました。ブートキャンプでもHaskellの様々な実用的なツールが紹介されましたが、その中にはソフトウェアトランザクションメモリなど、サービスに実際に導入してもいいと思えるものがたくさんありました。時間の都合上、各ツールに関する詳しい説明は聞けませんでしたが、Haskellの実用性を体験することができました。 今回のブートキャンプをはじめとする様々な取り組みが今後実を結び、次回はさらに面白いニュースを皆さんにお伝えできればと思います。例えば、LINEでのソフトウェア開発にHaskellをより本格的に導入し、それによって開発されたソフトウェアを技術面から詳しく解説できるようになれば何よりです。最後に、ブートキャンプ最終日の「モナド怪談」セッションで使われたスライドを以下に掲載しておきます。
↧