「48時間でSchemeを書こう」の版間の差分

削除された内容 追加された内容
en:Write Yourself a Scheme in 48 Hours (16:52, 5 November 2010 UTC) を翻訳
 
最初の一歩(部分)を追加
1 行
== 要旨 ==
Web上の殆どのHaskellチュートリアルは言語についてのマニュアルのような教え方をしようとしているようです。それらには言語の文法、概念が少し載っていて、読者に対話環境でいくつかの簡単な関数を作るように指示します。よく機能する有用なプログラムの書き方は大抵最後にまわされるか、そもそも省かれていたりします。
 
18 ⟶ 17行目:
 
ソースコードはこちら: [http://jonathan.tang.name/files/scheme_in_48/code/ listings]
 
== 最初の一歩 ==
 
まず、GHCをインストールしなければなりません。Linuxでは大抵もうインストール済かパッケージ・マネージャ(apt-getやyumなど)を通じて手に入ります。http://www.haskell.org/ghc/ からダウンロードすることもできます。バイナリパッケージをインストールするのが恐らく簡単でしょう。このチュートリアルはLinuxで開発されましたが、あなたがコマンドラインの使い方を知ってさえいればWindowsでも動くはずです。
 
UNIX(またはWindows Emacs)ユーザーには、非常によくできたEmacs modeがあり、syntax highlightingや自動インデントなどをやってくれます。Windowsユーザーはメモ帳や他のどんなテキストエディタを使うことができます。Haskellの文法は総じてメモ帳にやさしいですが、インデントには気を付けなければなりません。Eclipseユーザはeclipsefpプラグインを試してみるとよいかもしれません。最後に、GHCを使ったVisual StudioのHaskellプラグインもあります。
 
さあ、最初のHaskellプログラムを書く段になりました。このプログラムはコマンドラインから名前を読み込み挨拶を表示します。<code>.hs</code>で終わる名前のファイルを作って次のコードを打ち込んでください。正確にインデントするように気を付けてください。インデントを間違うとコンパイルできないかもしれません。
 
<syntaxhighlight lang="haskell">
module Main where
import System.Environment
 
main :: IO ()
main = do
args <- getArgs
putStrLn ("Hello, " ++ args !! 0)
</syntaxhighlight>
 
ではこのコードを見て行きましょう。最初の2行は我々が<code>System</code>モジュールをインポートする<code>Main</code>という名前のモジュールを作るということを指定しています。全てのHaskellプログラムは<code>Main</code>モジュールの中の<code>main</code>というアクションから始まります。<code>Main</code>モジュールは他のモジュールをインポートしても構いませんが、コンパイラが実行ファイルを作るために必ずなければなりません。Haskellはcase-sensitiveです。モジュール名は必ず大文字から始まり、定義は必ず小文字から始まります。
 
<code>main :: IO ()</code>という行は型宣言で、<code>main</code>は<code>IO ()</code>という型を持つことを示しています。<code>IO ()</code>はユニット型の値を持ち回るIOアクションで、ユニット型は<code>()</code>と表記される唯一の値を持つ、つまり何の情報も持ちません。Haskellでは型宣言は省略可能です。コンパイラが自動的に推論し、あなたが指定した型と違った時だけ文句を言います。このチュートリアルでは、明確さのため、全ての型を明記します。
 
IO型は''モナド''と呼ばれるものの一つです。モナドとは簡単な概念に難しい名前がついているだけです。基本的には、モナドは「私たちはある決まったやり方でいくらかの追加情報とともに値を持ち回り組合せますが、殆どの関数はそれについて気にしなくていいですよ」と言っています。''どのように''この追加情報を持ち回り、値を組合せるかがそのモナドをそれ自身たらしめるものです。モナドの持つ値はそれのまとう追加情報に関係無く普通の関数によって変えられたり、ある型から別の型に変換されたりするかもしれませんが、「パイプ」(値伝播機構)は同じままです。
 
この例では、「追加情報」は持ち回られる値を使って行われるIOアクションで、最終的な値は()です。<code>IO [String]</code>と<code>IO ()</code>はどちらも同じIOモナド型に属し、異なる基本型を持っています。それが意味するのは、それらがそれぞれ<code>[String]</code>、<code>()</code>の値を持ち回り、それに対して働くIOアクションだということです。このように基本型を包みこんでいるモナドの値は一般的に「アクション」と呼ばれます。IOモナドを、それぞれが外界に影響しながら、場合によっては持ち回される値に基いて働くことのできる動作(アクション)の連続として捉えるのが一番簡単だからです。
 
Haskellは関数型言語なので、プログラムを書くときは、コンピュータに実行すべき命令列を伝える代わりに、必要となるであろう全ての関数の定義を与えます。これらの定義はアクションと関数の様々な組合せを使います。コンパイラはそれら全てを纏め上げ、どのように実行すればいいかを探り出します。
 
定義は等式の形で書かれます。等式の左辺には名前と、変数を束縛する一つ以上の省略可能な''パターン''を書きます。右辺には他の定義の組み合わせを書き、コンピュータが左辺のパターンに出会した時に何をすべきかを定めます。このような等式はちょうど数学における普通の等式のように働きます。プログラムの中で左辺のパターンは右辺の式に置換可能であり、同じ値を得ることができます。これは「参照透過性」と呼ばれ、この特性は他の言語で書かれたプログラムに比べてHaskellプログラムが何をするのか判断するのを大分楽にしてくれます。
 
では、我々のmainアクションはどのように定義すればよいのでしょうか?<code>IO ()</code>アクションでなければならないのはわかっています。我々はコマンドライン引数を読み、出力を表示して、<code>()</code>を得たいのです。
 
IOアクションを作るには2つのやり方があります。
# <code>return</code>関数を使って普通の値をIOモナドに持ちあげる
# 2つの既にあるIOアクションを組み合わせる
今は2つのことをやりたいので、2番目の方法を用いましょう。組込みアクションの<code>getArgs</code>はコマンドライン引数を読み込み文字列のリストとして渡します。組み込みアクションの<code>putStrLn</code>は文字列を取りコンソールにその文字列を表示するというアクションを作ります。
 
これらのアクションを組み合わせるには、doブロックを使います。doブロックは<code>do</code>に続くインデントの揃った行の列で成ります。それぞれの行は2つの形のどちらかです。
# <code>name <- action1</code>
# <code>action2</code>
最初の形は<code>action1</code>の結果を後続のアクションで使えるよう<code>name</code>に束縛します。例えば、もし<code>action1</code>の型が<code>IO [String]</code>(<code>getArgs</code>のように文字列のリストを返すIOアクション)なら、<code>name</code>は"bind"演算子<code>>>=</code>によって後続の全てのアクションで持ち回される文字列のリストに束縛されます。二つ目の形はただ<code>action2</code>を実行し、もしあれば次の行に<code>>></code>演算子を使ってそれを渡します。bind演算子はそれぞれのモナドで異なる意味を持ちます。IOモナドでは、それはアクションの結果である外的な副作用を働きながらアクションを順番に実行します。bind演算子の意味付けは特定のモナドに依存するので、一つのdoブロックの中に異なるモナド型の動作を混在させることはできません。この場合なら、IOモナドのみ使用可能です。