実例によるPureScript

ウェブのための関数型プログラミング

Phil Freeman, "PureScript by Example - Functional Programming for the Web"

1 はじめに

1.1 関数型JavaScript

関数型プログラミングの手法は、かねてよりJavaScriptでも用いられてきました。

var sumOfPrimes = 
    _.chain(_.range(1000))
     .filter(isPrime)
     .reduce(function(x, y) { 
         return x + y; 
     })
     .value();
require('fs').readFile(sourceFile, function (error, data) {
  if (!error) {
    require('fs').writeFile(destFile, data, function (error) {
      if (!error) {
        console.log("File copied");
      }
    });
  }
});

関数は単純な抽象化を可能にし、優れた生産性をもたらしてくれます。しかし、JavaScriptでの関数型プログラミングには欠点があります。JavaScriptは冗長で、型付けされず、強力な抽象化を欠いているのです。また、奔放に書かれたJavaScriptコードは、式の理解をとても難しくします。

PureScriptはこのような問題を解決すべく作られたプログラミング言語です。PureScriptは、とても表現力豊かでありながらわかりやすく読みやすいコードを書けるようにする、軽量な構文を備えています。強力な抽象化を提供する豊かな型システムも使用しています。また、JavaScriptやJavaScriptへとコンパイルされる他の言語と相互運用するときに重要な、高速で理解しやすいコードを生成します。一言で言えば、PureScriptは純粋関数型プログラミングの理論的な強力さと、JavaScriptのお手軽で緩いプログラミングスタイルとの、とても現実的なバランスを狙っているということを理解して頂けたらと思います。

1.2 型と型推論

動的型付けの言語と静的型付けの言語をめぐる議論についてはよく知られています。PureScriptは静的型付けの言語、つまり正しいプログラムはコンパイラによってその動作を示すようなを与えられる言語です。逆にいえば、型を​​与えることができないプログラムは誤ったプログラムであり、コンパイラによって拒否されます。動的型付けの言語とは異なり、PureScriptでは型はコンパイル時のみに存在し、実行時には型の表現はありません。

PureScriptの型は、これまでJavaやC#のような他の言語で見たような型とは、いろいろな意味で異なっていることにも注意することが大切です。おおまかに言えばPureScriptの型はJavaやC#と同じ目的を持っているものの、PureScriptの型はMLとHaskellのような言語に影響を受けています。開発者がプログラムについての強い主張を表明できるので、PureScriptの型は表現力豊かなのです。最も重要なのは、PureScriptの型システムは型推論(type inference)をサポートしていることです。型推論は最低限の明示的な型注釈だけを必要とし、型システムを厄介者ではなく道具にしてくれます。簡単な例を示すと、次のコードはを定義していますが、それが Number型だという注釈はコードのどこにもありません。

iAmANumber = 
  let square x = x * x
  in square 42

次のもっと複雑な例では、コンパイラにとって未知の型が存在しているときでさえも、型注釈なしで型の正しさを確かめることができるということが示されています。

iterate f 0 x = x
iterate f n x = iterate f (n - 1) (f x)

ここで xの型は不明ですが、xがどんな型を持っているかにかかわらず、iterateが型システムの規則に従っていることをコンパイラは検証することができます。

静的型はプログラムの正しさについての確信を得るためだけではなく、その正しさによって開発を助けるということをあなたに納得させる(もしくは、あなたの理解を確認する)ことをこの本では試みます。最も単純な抽象化を使わないかぎりJavaScriptでコードの大規模なリファクタリングすることは難しいですが、型検証器のある表現力豊かな型システムは、リファクタリングさえ楽しく対話的な体験にするのです。

加えて、型システムによって提供されたこのセーフティネットは、より高度な抽象化をも可能にします。実際に、関数型プログラミング言語Haskellによって知られるようになった、型主導の強力な抽象化である型クラスをPureScriptは備えています。

1.3 多言語Webプログラミング

関数型プログラミングはすでに多くの成功を収めています。特に成功している応用例を いくつか挙げれば、データ解析、構文解析、コンパイラの実装、ジェネリックプログラミング、並列処理などです。

PureScriptのような関数型言語はアプリケーション開発の最初から最後までを​実施することが可能です。値や関数の型を提供することで既存のJavaScriptコードをインポートし、通常のPureScriptコードからこれらの関数を使用する機能をPureScriptは提供しています。この手法については本書で後ほど見ていくことになります。

しかしながら、PureScriptの強みのひとつはJavaScriptを対象とする他​​の言語との相互運用性にあります。アプリケーションの開発の一部にだけPureScriptを使用し、JavaScriptの残りの部分を記述するのに他の言語を使用するという方針もあります。

いくつかの例を示します。

この本では、小規模な課題をPureScriptで解決することに焦点を当てます。この解決策は大規模なアプリケーションに統合することもできますが、JavaScriptからPureScriptコードを呼び出す方法、およびその逆についても見ていきます。

1.4 ソフトウェア要件

この本でのソフトウェア要件は最小限です。第1章では開発環境のセットアップを一から案内します。これから使用するツールは、ほとんどの現代のオペレーティングシステムの標準リポジトリで使用できるものです。

PureScriptコンパイラ自体は最新のHaskell Platformが稼働しているシステムならソースからビルドすることができますが、次の章ではこの手順を説明していきます。

1.5 読者について

読者はJavaScriptの基本をすでに理解しているものと仮定します。すでにNPM、Grunt、Bower のようなJavaScriptのエコシステムでの経験があれば、自身の好みに応じて標準設定をカスタマイズしたい場合などに役に立ちますが、そのような知識は必要ではありません。

関数型プログラミングの予備知識は必要ありませんが、あっても害にはならないでしょう。実例には新しいアイデアがつきものですから、これから使う関数型プログラミングからこうした概念に対する直感的理解を形成することができるはずです。

PureScriptはプログラミング言語Haskellに強く影響を受けているため、Haskellに通じている読者はこの本の中で提示された概念や構文の多くに見覚えがあるでしょう。しかしながら、読者はPureScriptとHaskellの間にはいくつか重要な違いがあることも理解しておかなければなりません。ここで紹介する概念の多くはHaskellでも同じように解釈できるとはいえ、どちらかの言語での考え方を他方の言語でそのまま応用しようとすることは、必ずしも適切ではありません。

1.6 本書の読み進めかた

本書の各章は、概ね章ごとに完結しています。しかしながら、多少の関数型プログラミングの経験がある初心者でも、まずは各章を順番に進めていくことをおすすめします。最初の数章では、本書の後半の内容を理解するために必要な基礎知識を養います。関数型プログラミングの考え方に十分通じた読者(特にMLやHaskellのよう強く型付けされた言語での経験を持つ読者)なら、本書の前半のほうの章を読まなくても後半の章のコードの大まかな理解を得ることがおそらく可能でしょう。

各章ではそれぞれひとつの実用的な例に焦点をあて、新しい考え方を導入するための動機付けとして用います。各章のコードは本書のGitHubのリポジトリから入手できます。各章にはソースコードから抜粋したコード片が掲載されていますが、完全に理解するためには本書に掲載されたコードと平行してリポジトリのソースコードを読む必要があります。対話式環境 psciで実行し理解を確かめられるように、長めの節には短いコード片が含まれます。

コード例は次のように等幅フォントで示されています。

module Example where

main = Debug.Trace.trace "Hello, World!"

先頭にドル記号がついた行は、コマンドラインに入力されたコマンドです。

$ psc src/Main.purs

通常はこれらのコマンドはLinux / Mac OSの利用者には適合しますが、Windowsの利用者はファイル区切り文字を変更する、シェルの組み込み機能をWindowsの相当するものに置き換えるなどの小さな変更を加える必要があるかもしれません。

psci 対話式プロンプトに入力するコマンドは、行の先頭に山括弧が付けられています。

> 1 + 2
3

各章には演習が付いており、それぞれ難易度も示されています。各章の内容を完全に理解するために、演習に取り組むことを強くお勧めします。

この本は初心者にPureScriptへの導入を提供することを目的としており、問題についてのお決まりの解決策の一覧を提供するような種類の本ではありません。初心者にとってこの本を読むのは楽しい挑戦になるはずですし、本書の内容を読み演習に挑戦すればだいたいの利益を得られるでしょうが、なにより重要なのは、あなたが自分自身のコードを書いてみることです。

1.7 困ったときには

もしどこかでつまづいたときには、PureScriptを学ぶためのオンラインで利用可能な資料がたくさんあります。

もしあなたが例を読んで学ぶことを好むなら、GitHubの purescriptpurescript-contrib organisation にはPureScriptコードの例がたくさんあります。

1.8 著者について

私はPureScriptコンパイラの最初の開発者です。私はカリフォルニア州ロサンゼルスを拠点にしており、8ビットパーソナルコンピュータ、Amstrad CPC上のBASICでまだ幼い時にプログラミングを始めました。それ以来、私はいくつものプログラミング言語(JavaやScala、C#、F#)で業務に携わってきました。

プロとしての経歴が始まって間もなく、私は関数型プログラミングと数学の関係を理解するようになり、そしてプログラミング言語Haskellとの恋に落ちました。

JavaScriptでの経験をもとに、私はPureScriptコンパイラの開発を始めることにしました。私がHaskellのような言語から取り上げた関数型プログラミングの手法を使っていることを私自ら発見しましたが、それを応用するためのもっと理にかなった環境を求めていました。そのときの解決策にはHaskellをその意味論を維持しながらJavaScriptへとコンパイルするいろいろな試み(Fay、Haste、GHCJS)が含まれていましたが、私が興味を持っていたのは、この問題への別の切り口からのアプローチ、すなわちHaskellのような言語の構文と型システムを楽しみながらJavaScriptの意味論も維持するということが、どのようにすれば可能になるのかでした。

私はウェブサイトを運営しており、Twitterで連絡をとることもできます。

1.9 謝辞

現在の状態に到達するまでPureScriptを手伝ってくれた多くの協力者に感謝したいと思います。コンパイラやツール、ライブラリ、ドキュメント、テストでの組織的で弛まぬ努力がなかったら、プロジェクトは間違いなく失敗していたことでしょう。

この本の表紙に表示されたPureScriptのロゴはGareth Hughesによって作成されたもので、Creative Commons Attribution 4.0 licenseの条件の下で再利用させて頂いています 。

最後に、この本の内容に関する反応や訂正をくださったすべての方に、心より感謝したいと思います。