Warning : Undefined variable $type in /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php on line 3
Warning : "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /home/users/1/sub.jp-asate/web/wiki/includes/json/FormatJson.php on line 297
Warning : Trying to access array offset on value of type bool in /home/users/1/sub.jp-asate/web/wiki/includes/Setup.php on line 660
Warning : session_name(): Session name cannot be changed after headers have already been sent in /home/users/1/sub.jp-asate/web/wiki/includes/Setup.php on line 834
Warning : ini_set(): Session ini settings cannot be changed after headers have already been sent in /home/users/1/sub.jp-asate/web/wiki/includes/session/PHPSessionHandler.php on line 126
Warning : ini_set(): Session ini settings cannot be changed after headers have already been sent in /home/users/1/sub.jp-asate/web/wiki/includes/session/PHPSessionHandler.php on line 127
Warning : session_cache_limiter(): Session cache limiter cannot be changed after headers have already been sent in /home/users/1/sub.jp-asate/web/wiki/includes/session/PHPSessionHandler.php on line 133
Warning : session_set_save_handler(): Session save handler cannot be changed after headers have already been sent in /home/users/1/sub.jp-asate/web/wiki/includes/session/PHPSessionHandler.php on line 140
Warning : "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /home/users/1/sub.jp-asate/web/wiki/languages/LanguageConverter.php on line 773
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/Feed.php on line 294
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/Feed.php on line 300
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/WebResponse.php on line 46
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/WebResponse.php on line 46
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/WebResponse.php on line 46
https:///mymemo.xyz/wiki/api.php?action=feedcontributions&feedformat=atom&user=223.219.238.203
miniwiki - 利用者の投稿記録 [ja]
2024-06-28T23:42:56Z
利用者の投稿記録
MediaWiki 1.31.0
Haskell
2018-08-05T22:49:44Z
<p>223.219.238.203: /* 実装 */ 言語拡張について言及</p>
<hr />
<div>{{Infobox プログラミング言語<br />
|名前 = Haskell<br />
|パラダイム = [[関数型言語]]<br />
|logo = [[File:Haskell-Logo.svg|150px]]<br />
|設計者 = [[サイモン ペイトン ジョーンズ]]、[http://www.cs.yale.edu/homes/hudak-paul/ Paul Hudak]、[[Philip Wadler]] 他<br />
|開発者 = <br />
|最新リリース = <br />
|型付け = 強い[[静的型付け]]<br />
|処理系 = [[ghc]]、[[Hugs]]、[http://www.cs.york.ac.uk/fp/nhc98/ NHC]、[http://repetae.net/john/computer/jhc/ JHC]、[[Yhc]]<br />
|影響を受けた言語 = {{lang|en|[[Miranda]]}}<br />
|影響を与えた言語 = {{lang|en|[[Factor]]}}<br />
}}<br />
<br />
{{プログラミング言語}}<br />
{{lang|en|'''Haskell'''}}(ハスケル)は[[評価戦略#正格でない評価|非正格]]な評価を特徴とする[[関数型言語|純粋関数型プログラミング言語]]である。名称は[[数学者]]であり[[論理学者]]である[[ハスケル・カリー]]に由来する。<br />
<br />
== 概要 ==<br />
{{lang|en|Haskell}} は[[高階関数]]や[[静的型付け|静的]][[ポリモーフィズム|多相型付け]]、[[定義]]可能な[[演算子#プログラミングにおける演算子|演算子]]、例外処理といった多くの言語で採用されている現代的な機能に加え、パターンマッチングや[[カリー化]]、リスト内包表記、[[ガード (プログラミング)|ガード]]といった多くの特徴的な機能を持っている。また、[[遅延評価]]や[[再帰]]的な[[サブルーチン#関数|関数]]や[[代数的データ型]]もサポートしているほか、独自の概念として[[圏論]]のアイデアを利用し[[参照透過性]]を壊すことなく副作用のある操作(例えば [[変数 (プログラミング)#代入|代入]]、[[入出力#コンピュータ処理における入出力|入出力]]、[[配列]]など)を実現する[[モナド (プログラミング)|モナド]]を含む。このような機能の組み合わせにより、[[手続き型プログラミング言語]]では記述が複雑になるような処理がしばしば簡潔になるばかりではなく、必要に応じて手続き型プログラミングを利用できる。<br />
<br />
{{lang|en|Haskell}} は関数型プログラミングの研究対象として人気が高い。あわせて {{lang|en|Parallel Haskell}} と呼ばれる[[マサチューセッツ工科大学]]や[[グラスゴー大学]]によるものをはじめ、他にも {{lang|en|Distributed Haskell}}<ref>かつては {{lang|en|Goffin}} と呼ばれていた。</ref> や {{lang|en|Eden}} といった分散バージョン、{{lang|en|Eager Haskell}} と呼ばれる[[投機的実行]]バージョン、{{lang|en|Haskell++}} や {{lang|en|O'Haskell}}、{{lang|en|Mondrian}} といった[[オブジェクト指向]]バージョンも複数存在しているなど、いくつもの派生形が開発されてきている。<br />
<br />
[[グラフィカルユーザインターフェース|GUI]]開発向けサポートの新しい方法を提供する、{{lang|en|[[Clean|Concurrent Clean]]}} と呼ばれる {{lang|en|Haskell}} に似た言語もある。{{lang|en|Haskell}} と {{lang|en|Concurrent Clean}} の最大の違いは、[[モナド (プログラミング)|モナド]]を代用する一意型の採用である。<br />
<br />
{{lang|en|Haskell}} は比較的小規模なユーザコミュニティを持つが、その力はいくつかのプロジェクトで十分に生かされてきた。オードリー・タングによる {{lang|en|Pugs}} は近く{{いつ|date=2013年11月}}リリース予定である {{lang|en|Perl6}} の[[インタプリタ]]と[[コンパイラ]]の実装で、書くのにたったの数ヶ月しかかからなかったなど {{lang|en|Haskell}} の有用性を証明するものである。Darcs はさまざまな革新的な機能を含むリビジョンコントロールシステムである。{{lang|en|[[Linspire]] Linux}} は {{lang|en|Haskell}} をシステムツール開発に選択した。<br />
<br />
{{lang|en|Haskell}} の急速な進化は今も継続しており、[[GHC]]は現在の[[デファクトスタンダード|事実上の標準]]の処理系であるといえる。<br />
<br />
{{lang|en|ICFP Programming Contest}} では[[2001年]]、[[2004年]]、[[2005年]]に最優秀賞に選ばれている。<br />
<br />
{{仮リンク|Cabal (ソフトウェア)|en|Cabal (software)|label=Cabal}} は {{lang|en|Haskell}} 用のビルドおよびパッケージングシステムである。{{lang|en|Cabal}} を利用して {{lang|en|Haskell}} ライブラリのアーカイブである {{lang|en|Hackage}} を参照して、新たなパッケージを簡単にインストールすることもできる。しばしばパッケージの{{仮リンク|依存地獄|en|Dependency hell}}が発生しがちだが、それらを解決するため、{{ill2|Stack (Haskell)|en|Stack (Haskell)|label=Stack}}という環境も有志によって提供されている。<br />
<br />
== 歴史 ==<br />
[[1985年]]、遅延関数言語である {{lang|en|Miranda}} がリサーチ・ソフトウェア社によって発表された。[[1987年]]にはこのような非正格な純粋関数型プログラミング言語が十二以上存在していたが、そのうち最も広く使われていた {{lang|en|Miranda}} は[[パブリックドメイン]]ではなかった。[[ポートランド (オレゴン州)|オレゴン州ポートランド]]で開催された {{lang|en|Functional Programming Languages and Computer Architecture}} ({{lang|en|FPCA '87}}) において開かれた会議中に、遅延関数型言語のオープンな標準を作成するための委員会が発足されるべき、という強い合意が参加者のあいだで形成された。委員会の目的は、関数型言語のデザインにおける将来の研究のための基礎として役立つ共通のものへと、既存の関数型言語を統合することであった<ref name="Pref 98">{{cite web|url=http://haskell.org/onlinereport/preface-jfp.html|title=Preface|work=Haskell 98 Language and Libraries: The Revised Report|year=2002|month=December|accessdate=2009-06-23}}</ref>。<br />
<br />
==={{lang|en|Haskell}} 1.0===<br />
最初の版の {{lang|en|Haskell}}({{lang|en|Haskell}} 1.0)は[[1990年]]に作成された<ref>{{cite web|url=http://www.haskell.org/haskell-history.html|title=The History of Haskell|accessdate=2009-06-23}}</ref>。委員会の活動は一連の言語仕様を結果に残し、[[1997年]]後半にそのシリーズは、安定しており小さく可搬なバージョンの言語仕様と、学習用および将来の拡張の基礎としての基本ライブラリなどを定義した{{lang|en|Haskell}} 98 に到達した。実験的な機能の付加や統合を通じて {{lang|en|Haskell}}98 の拡張や派生物を作成することを、委員会ははっきりと歓迎した<ref name="Pref 98" />。<br />
<br />
==={{lang|en|Haskell}} 98===<br />
[[1999年]]2月、{{lang|en|Haskell}} 98 言語標準は最初に {{lang|en|The Haskell 98 Report}} として発表された<ref name="Pref 98" />。[[2003年]]1月、改定されたバージョンが {{lang|en|Haskell 98 Language and Libraries: The Revised Report}} として発表された<ref name="Revised report">{{cite web|url=http://haskell.org/onlinereport/|title=Haskell 98 Language and Libraries: The Revised Report|year=2002|month=December|author=Simon Peyton Jones (編)|authorlink=Simon Peyton Jones|accessdate=2009-06-23}}</ref>。GHC に代表されるように、{{lang|en|Haskell}} は急速に発達しつづけている。<br />
<br />
==={{lang|en|Haskell′}}===<br />
[[2006年]]前半、非公式に {{lang|en|'''Haskell′'''}}({{lang|en|Haskell Prime}})と呼ばれている {{lang|en|Haskell}} 98 {{lang|en|standard}} の後継の作成プロセスが開始された<ref>{{cite web|url=http://haskell.org/haskellwiki/Future|title=Future development of Haskell|accessdate=2009-06-23}}</ref>。このプロセスは {{lang|en|Haskell}} 98 のマイナーバージョンアップを目的としている<ref>{{cite web|url=http://hackage.haskell.org/trac/haskell-prime|title=Welcome to Haskell'|work=The Haskell' Wiki|accessdate=2009-06-23}}</ref>。<br />
<br />
==={{lang|en|Haskell}} 2010===<br />
{{lang|en|Haskell}} 2010 は他のプログラミング言語とのバインディングを可能にする {{lang|en|Foreign Function Interface}}(FFI)を {{lang|en|Haskell}} に追加し, いくつかの構文上の問題を修正する(正式な構文を変更する)。「<code>n + k</code> パターン」と呼ばれる構文を削除し, <code>fak (n+1) = (n+1) * fak n</code>というような形式の定義はもはや許されない。{{lang|en|Haskell}} に {{lang|en|Haskell}} 2010 のソースかどうかや, いくつかの必要な拡張の指定を可能にする言語プラグマ構文拡張<ref>{{lang-en-short|language-pragma-syntax-extension}}</ref>を導入する。{{lang|en|Haskell}} 2010 に導入された拡張の名前は, <code>DoAndIfThenElse</code>, <code>HierarchicalModules</code>(階層化ライブラリ), <code>EmptyDataDeclarations</code>, <code>FixityResolution</code>, <code>ForeignFunctionInterface</code>, <code>LineCommentSyntax</code>, <code>PatternGuards</code>, <code>RelaxedDependencyAnalysis</code>, <code>LanguagePragma</code>, <code>NoNPlusKPatterns</code> である。<br />
<br />
== 主な構文 ==<br />
<br />
ここでは以降の解説を読む上で必要な {{lang|en|Haskell}} の文法規則を簡単に説明する。{{lang|en|Haskell}} の構文は数学で用いられるものによく似せられていることに注意されたい。<br />
<br />
=== 型 ===<br />
{{lang|en|Haskell}} の型名は先頭が大文字でなければならない。標準で存在する単純なデータ型としては、<code>Bool</code>(真偽値)型、<code>Int</code>(整数)型、<code>Float</code>(単精度浮動小数点数)型、<code>Char</code>(文字)型、<code>String</code>(文字列)型などが予め定義されている。任意の式の型を定義するには、式と型の間に <code>::</code> 記号をおく。また、変数などのシンボル<ref>「変数」は実際にはその値を動的に変更することはできないなど、C言語など手続き型言語の変数とは明らかに異なるものである。</ref>を定義する際に変数名を式に指定して書くことで、その変数の型を指定することができる。例えば、次は[[円周率]]および[[ネイピア数]]を定数として定義し、さらにその型も浮動小数点型として指定している。<ref>ここでは説明のため単に Float としているが、標準ライブラリで定義されている円周率 <code>pi</code> は浮動小数点数型の抽象的な型クラスである <code>Floating a</code> で定義されており、<code>Float</code> のみならず 倍精度浮動小数点数型 <code>Double</code> の値としても取得できる。</ref><br />
<br />
<source lang="haskell">pi :: Float<br />
pi = 3.1415926535<br />
<br />
e = 2.7182818284 :: Float -- 式の中でその型を指定している<br />
</source><br />
<br />
関数の型は、各引数の間を <code>-&gt;</code> 記号で区切って表記する。関数は引数をひとつ適用するたびに、その型は <code>-&gt;</code> で区切られたうちの一番左が消えた型となると考えればよい。<ref>{{lang|en|Haskell}} はカリー化によりすべての関数を 1 引数の関数として表現できるが、これにしたがって <code>-&gt;</code> は右結合であるとして読むこともできる。上記の関数の型の定義は、括弧を明示した次の定義と同等である。<br />
<br />
<source lang="haskell">gcd :: Int -> (Int -> Int)</source><br />
<br />
関数を引数にとる関数は、引数の型を括弧で囲んで -> 記号の優先順位を指定すればよい。次は関数を二つとり、その合成関数を返す演算子 . の定義である。<br />
<br />
<source lang="haskell">(.) :: (b -> c) -> (a -> b) -> a -> c</source><br />
</ref>例えば、ふたつの整数を引数にとりその最大公約数を返す関数 <code>gcd</code> の型は次のように定義される。<ref>式の外で演算子の型を指定するときは、演算子を括弧で囲めばよい。以下の関数と演算子の相互変換を参照のこと。</ref><br />
<br />
<source lang="haskell">gcd :: Int -> Int -> Int -- 関数名 :: 引数1の型 -> 引数2の型 -> 返り値の型</source><br />
<br />
型変数を使い、型を抽象化することもできる。これは {{lang|en|C++}} のテンプレートや {{lang|en|Java}} のジェネリクスに相当するが、様々な種(型の型)に適用できるためより柔軟である。例えば、<code>a</code> 型の値をとり、それをそのまま返す恒等関数 <code>id</code> を型の指定とともに定義すると以下のようになる。ここで任意の型を示す型名 <code>a</code> が定義に使われているが、このように先頭が小文字で始まっている型名は具体的な型名ではなく型変数である。この関数はあらゆる型の値を引数にとることができる。<br />
<br />
<source lang="haskell">id :: a -> a<br />
id x = x<br />
</source><br />
<br />
また、データ型はパラメータとして型変数を持つことができる。例えば、スタックやハッシュテーブルなどのデータ型は、その要素の型を型変数として定義する。ハッシュテーブルを実装するデータ型 <code>HashMap :: * -> * -> *</code> があり、キーに文字列(<code>String</code>)、値に整数(<code>Int</code>)を持つハッシュテーブル <code>hashtable</code> の型は次のようになる。<ref><br />
これは、{{lang|en|C++}} や {{lang|en|Java}} のような言語では次のようなコードに相当する。<br />
<br />
<source lang="Java">Hashtable<String,Int> hashtable;</source><br />
</ref><br />
<br />
<source lang="haskell">hashtable :: HashMap String Int</source><br />
<br />
そのほか、特殊な表記を持つ型として[[リスト]]と[[タプル]]、[[文字列]]がある。リストは {{lang|en|Haskell}} で極めて頻繁に用いられるため、[[糖衣構文|特別な構文]]が用意されている。リストは要素の型を角括弧で囲む。次は <code>Char</code>(文字)のリストを束縛する変数 <code>text</code> の定義である。<ref>当然ながら、リストの要素としてリストを持つこともできる。例えば、文字リスト(文字列)のリストの型は <code>&#91;&#91;Char&#93;&#93;</code> となるであろう。</ref>文字のリストは文字列と等価である。2行目にあるように、文字列は殆どのプログラミング言語と同じように二重引用符で囲む。コメントにHaskellでのリストの表記を添えた。最後にデシュガー(糖衣構文を元の表記に戻すこと)したリストの表記法を示した。textもhelloも等価である。<br />
<br />
<source lang="haskell">text :: [Char]<br />
text = "Hello" -- ['H','e','l','l','o'] の糖衣構文<br />
hello :: [Char]<br />
hello = 'H':'e':'l':'l':'o':[]</source><br />
<br />
タプルは要素の型をカンマで区切り、括弧で囲む。次は <code>Float</code>(浮動小数点数)の値をもつ2次元座標のタプルが束縛される変数 <code>point</code> の定義である。<br />
<br />
<source lang="haskell">point :: (Float,Float)<br />
point = (3, 7)</source><br />
<br />
タプルは2以上ならいくつでも要素を持つことができる。1要素のタプルは優先順位の括弧と表記が衝突し、また用途がないので存在しない。要素数がゼロのタプルは特に「ユニット」(<code>Unit</code>) と呼ばれ、有効な値を持たないなどの時に使われる。<ref>ユニットはC言語や {{lang|en|Java}} などでいう <code>void</code> のような使われ方をする。</ref><br />
<br />
<code>type</code> キーワードを用いて、型に別名をつけることができる。<ref>言い換えれば、単純な型名に見えても何か複雑な別の型の別名である可能性がある。</ref>次は <code>&#91;Char&#93;</code> に <code>String</code> という別名をつけている。<ref>実際に標準ライブラリでは <code>String</code> は <code>&#91;Char&#93;</code> の別名であり、<code>String</code> にはあらゆるリストの操作が可能である。</ref><br />
<br />
<source lang="haskell">type String = [Char]<br />
text :: String<br />
</source><br />
<br />
=== 関数と演算子 ===<br />
<br />
関数名は先頭が小文字でなければならず、記号を含むことはできない。演算子名は記号のみで構成されていなければならない。関数の定義ではC言語のような引数を囲む括弧や区切りのカンマは使われず、単に引数を空白文字で区切って表記する。次は先程示した恒等関数 <code>id</code> に、型の定義に加えて本体の定義もした例である。<br />
<br />
<source lang="haskell">id :: a -> a<br />
id x = x -- 関数名 仮引数1 仮引数2 &hellip; = 関数本体の式<br />
</source><br />
<br />
関数の適用も同様で、単に関数に続いて空白文字で区切った引数を並べればよい。以下では上記の恒等関数 <code>id</code> を(ここでは使う必要はないが)適用して、別の変数を定義した例である。<br />
<br />
<source lang="haskell">hoge = id "piyo" -- hoge == "piyo" となる。<br />
</source><br />
<br />
引数がつねに2個であることや引数の間に演算子をおくことなどを除けば、演算子についても関数の定義や適用と同様である。標準で定義されている算術演算子を使って、[[ボディマス指数|BMI]]を計算する関数 <code>bmi</code> を定義してみる。<br />
<br />
<source lang="haskell">bmi :: Float -> Float -> Float<br />
bmi weight height = weight / height ^ 2</source><br />
<br />
この定義では <code>Float</code> を引数にとり <code>Float</code> で結果を返すが、この関数では <code>Double</code> を引数に使うことはできない。どの浮動小数点数型でも扱えるような関数にするには、次のように型変数を使えばよい。<br />
<br />
<source lang="haskell">bmi :: Floating a => a -> a -> a<br />
bmi weight height = weight / height ^ 2</source><br />
<br />
このバージョンの関数 <code>bmi</code> では引数や返り値の型が <code>a</code> とされているが、これにさらに <code>a</code> は <code>Floating</code> であるとの制約をつけている。<code>Floating</code> は <code>Float</code> や <code>Double</code> を抽象化する型クラスであり <code>/</code> や <code>^</code> といった演算子を適用できるので、<code>bmi</code> の定義においてこれらの演算子を使うことができている。また、整数などの値は引数に取れないし、返り値は引数に与えた型で戻ってくる。<br />
<br />
関数と演算子は新たに定義し直さなくても相互に変換可能である。関数を演算子として使うには、関数を <code>`</code> (バッククォート)で囲む。逆に、演算子を関数として使うには括弧で囲む。例えば、整数を除算する関数 <code>div</code> はよく演算子として使われる<ref>除算する演算子 <code>/</code> は存在するが、これは <code>Float</code> などを除算する演算子であり整数ではない。他の言語のように自動的に値を変換する(int → float など)ような動作は {{lang|en|Haskell}} では意図的に排除されている。</ref>。<br />
<br />
<source lang="haskell">aspectRatio = width `div` height</source><br />
<br />
なお、関数適用の優先順位はすべての[[演算子の優先順位]]よりも高い。<br />
<br />
== 特徴的な機能 ==<br />
ここではあまり他の言語では見られない {{lang|en|Haskell}} 特有の機能を中心に解説する。ここでは説明していないが、現代的な実用言語では常識となっている[[ガーベジコレクション]]、[[例外処理]]、[[モジュール]]、外部関数の呼び出し、[[正規表現]]ライブラリなどの機能は、{{lang|en|Haskell}} にも当然存在している。<br />
<br />
===遅延評価===<br />
{{lang|en|Haskell}} は[[遅延評価]]を基本的な評価戦略とする。ほとんどの言語では関数の呼び出しにおいて引数に与えられたすべての式を評価してから呼び出された関数に渡す[[先行評価]]を評価戦略とするが、これに対し {{lang|en|Haskell}} ではあらゆる式はそれが必要になるまで評価されない。次の定数 <code>answer</code> は評価すると常に <code>42</code> を返すが、その定義には未定義の式を含む。<br />
<br />
<source lang="haskell"><br />
answer = const 42 (1 `div` 0)<br />
</source><br />
<br />
ここで、<code>const</code> は常に第1[[引数]]を返す[[定数]]関数である。また、<code>`div`</code> は整数の除算を行う演算子であり、<code>1 `div` 0</code> は <code>1 / 0</code> に相当し、この値は未定義であり、この部分を評価すればエラーになる。正格評価をする言語でこのような式を評価しようとすると、[[ゼロ除算]]によるエラーになるであろう。しかし 上記の定数 <code>answer</code> を評価してもエラーにはならない。<code>const</code> は第1引数をつねに返すので第2引数を評価する必要はなく、第2引数に与えられた式 <code>1 `div` 0</code> は無視されるので評価されないからである。遅延評価がデフォルトで行われることにより、不要な計算は省かれ、[[参照透過性]]により同じ式を複数回評価する必要もなくなるため、{{lang|en|Haskell}} では最適化によって計算効率の向上が期待できる場合がある。ただし、頻繁に新たな値を計算する場合は正格評価のほうが効率がよく、必要に応じて<code>seq</code>関数やBangPatterns拡張による明示により正格評価もできる。<br />
<br />
=== 型推論 ===<br />
{{lang|en|Haskell}} では[[サブルーチン#関数|関数]]の[[データ型]]を明示しなくても処理系が自動的に型を推論する。以下は型の宣言を省略し、本体のみを宣言した引数の平方を返す関数 <code>square</code> である。<br />
<source lang="haskell"><br />
square x = x * x<br />
</source><br />
この場合 <code>square</code> の型は型推論され、次のように明示的に型を宣言したのと同じになる。<br />
<source lang="haskell"><br />
square :: (Num a) => a -> a<br />
square x = x * x<br />
</source><br />
この宣言は、「<code>Num</code>のインスタンス<ref>{{lang|en|Haskell}} の型システムにおける汎用型が実体化されたもの。オブジェクト指向におけるインスタンスとは異なる。</ref>である <code>a</code> の型の値を引数にとり、<code>a</code> の型の値を返す」と読める。ここでは「<code>*</code>」[[演算子#プログラミングにおける演算子|演算子]]が適用可能な最も広い型である <code>Num a</code> が選択されており、整数や浮動小数点数、有理数のような <code>Num</code> のインスタンスであるあらゆる型の値を渡すことができる。外部に公開するような関数を定義するときは、型推論によって自動的に選択される最も広い型では適用可能な範囲が広すぎる場合もある。<code>Integer</code> のみを渡せるように制限する場合は、次のように明示的に型を宣言すればよい。<br />
<source lang="haskell"><br />
square :: Integer -> Integer<br />
square x = x * x<br />
</source><br />
型推論のため、{{lang|en|Haskell}} は型安全でありながらほとんどの部分で型宣言を省略できる<ref>ただし、型を明示することは可読性を向上したり問題の発見に役立つため、常に省略するのがよいとは限らない。</ref>。なお、次のコードは型宣言が必要な例である。<code>read</code> は文字列をその文字列があらわすデータ型に変換する抽象化された関数である。<br />
<source lang="haskell"><br />
main = print (read "42") -- コンパイルエラー!<br />
</source><br />
このコードはコンパイルエラーになる。<code>read</code> は複数のインスタンスで実装されており、数値なら数値型に変換する <code>read</code>、リストならリストに変換する <code>read</code> というように型ごとに実装が存在する。{{lang|en|Haskell}} の型は総て静的に決定されなければならない。このコード場合、プログラマは <br />
<source lang="haskell"><br />
read :: String -> Int<br />
</source><br />
という型をもつ実装の <code>read</code> が選択されると期待しているであろうが、これはコンパイラによる静的な型検査では決定できない。つまり、{{lang|en|Haskell}} コンパイラは <code>read</code> の返り値を受け取っている関数 <code>print</code> の型を検査し多数の実装の中から適切な <code>read</code> を選択しようとするが、<code>print</code> は <code>Show</code> のインスタンスが存在するあらゆる型を引数にとるため、型推論によっても <code>read</code> の型を一意に決定できない。これを解消するひとつの方法は、<code>::</code> によって型を明示することである。<br />
<source lang="haskell"><br />
main = print ((read "42") :: Int) -- コンパイル成功!read の返り値を Int と明示している<br />
</source><br />
また、そもそも <code>read</code> の返り値を整数型しか取らない関数に与えていればあいまいさは生じず、型推論は成功する。<br />
<source lang="haskell"><br />
printIntOnly :: Int -> IO ()<br />
printIntOnly x = print x<br />
<br />
main = printIntOnly (read "42") -- コンパイル成功!<br />
</source><br />
他の言語、たとえば {{lang|en|Java}} でこのような抽象的な関数を書こうとしても、{{lang|en|Java}} では返り値の値の型によって関数を選択するようなことはできない(引数の型によって選択するメソッドのオーバーロードは存在する)。そのため、関数の実装ごとに別の名前をつけてプログラマに明示的に選択させて解決させることになる<ref><code>java.lang.Integer.parseInt</code>、<code>java.lang.Double.parseDouble</code> など</ref>。この方法は簡潔でわかりやすいが、抽象性の高さに基づく再利用性という点では {{lang|en|Haskell}} のような多相には劣ってしまう。<br />
<br />
=== 代数的データ型 ===<br />
{{lang|en|Haskell}} のデータ型には[[代数的データ型]]<ref>{{lang-en-short|algebraic data type}}</ref>と呼ばれる、C言語などでいう構造体や列挙体の性質を兼ね備えたものが用いられる。次の例は二つのInt型の値をフィールドに持つ二次元の座標、<code>Point2D</code> 型を定義したもので、これは代数的データ型の構造体的な性質を示す。先頭のトークン「<code>data</code>」は代数的データ型の宣言であることを示す予約語である。ここで最初の <code>Point2D</code> はデータ型名を表し、次の <code>Point2D</code> はデータコンストラクタ名を示す<ref>データ型名とデータコンストラクタ名は同じでも構わない。このような単純な代数的データ型であれば型名とデータコンストラクタ名を同じにすることも多い。</ref>。<source lang="haskell"><br />
data Point2D = Point2D Int Int<br />
<br />
origin :: Point2D<br />
origin = Point2D 0 0 -- データコンストラクタは関数のように適用できる<br />
</source><br />
<br />
データコンストラクタは値を定義するための特殊な関数といえる。データコンストラクタは後述するパターンマッチによって値を取り出す際にも用いられる。<br />
<br />
次の例は[[トランプ]]の4つの[[スート|スーツ]]を示す <code>Suit</code> 型を定義したものである。<code>Spade</code>、<code>Heart</code>、<code>Club</code>、<code>Diamond</code> の4つのデータコンストラクタが定義されており、<code>Suit</code> 型の式は <code>Spade</code>、<code>Heart</code>、<code>Club</code>、<code>Diamond</code> のいずれかの値をとる。<br />
<source lang="haskell"><br />
data Suit = Spade | Heart | Club | Diamond<br />
</source><br />
次の例は <code>String</code> 型の値を格納する[[二分木]]の型である。<code>Leaf</code>(葉)は <code>String</code> 型の値を持ち、<code>Branch</code>(枝)は <code>Leaf</code> もしくは <code>Branch</code> である <code>Tree</code> 型の変数を二つ持つ。これは代数的データ型の構造体的な性質と列挙体的な性質の両方が現れている。<br />
<source lang="haskell"><br />
data Tree = Leaf String | Branch Tree Tree<br />
<br />
organisms :: Tree<br />
organisms = Branch animals plants -- Branch (Branch (Branch (Leaf "Lion") (Leaf "Tiger")) (Leaf "Wolf")) (Leaf "Cherry")<br />
<br />
animals = Branch cats (Leaf "Wolf")<br />
<br />
cats = Branch (Leaf "Lion") (Leaf "Tiger")<br />
<br />
plants = Leaf "Cherry"<br />
</source><br />
<br />
GADTと呼ばれる機能を使うと、コンストラクタの型を明示してデータ型を定義することもできる。<br />
<br />
<source lang="haskell"><br />
{-# LANGUAGE GADTs #-}<br />
data Male<br />
data Female<br />
<br />
data Person s where<br />
Adam :: Person Male<br />
Eve :: Person Female<br />
Child :: Person Male -> Person Female -> Int -> Person a<br />
</source><br />
<br />
=== 無名関数 ===<br />
{{lang|en|Haskell}} は[[第一級関数]]をサポートしており、[[高階関数]]を定義することができる。つまり、関数の引数として関数を与えたり、返り値として関数を返すことができる。無名関数は <code>\</code> (バックスラッシュ)から始まり、それに続いて引数となる変数、引数と本体のあいだに <code>-&gt;</code> 記号をおく。<code>List</code> モジュールに定義されているリストのソートを行う関数 <code>sortBy</code> は、二つの要素に大小関係を与える関数を引数にとるが、無名関数を使うと例えばリストをその長さの短い順にソートする関数 <code>sortList</code> は次のように定義できる。<br />
<source lang="haskell"><br />
sortList xs = sortBy (\as bs -> compare (length as) (length bs)) xs<br />
</source><br />
<br />
=== 関数のカリー化と部分適用 ===<br />
{{lang|en|Haskell}} において、2つの引数を持つ関数は、1つの引数をとり「1つの引数をとる関数」を返す関数と同義である。このように、関数を返すことで全ての関数を1つの引数の関数として表現することを[[カリー化]]という。(あえてタプルを使うことで複数の引数を渡すような見かけにすることもできるが。)次の例は2つの引数をとり、そのうち値が大きい値を返す関数 <code>max</code> である。<br />
<source lang="haskell"><br />
max a b = if a > b then a else b<br />
</source><br />
この関数maxは無名関数を用いて次のように書き換えることができる。先ほどの表現とまったく同様に動作するが、この表現では関数を返す様子がより明らかになっている。<source lang="haskell"><br />
max a = \b -> if a > b then a else b<br />
</source><br />
さらに、次のようにも書き換えることができる。<br />
<source lang="haskell"><br />
max = \a -> \b -> if a > b then a else b<br />
</source><br />
あるいは、<code>f x = ... x ... </code>は、<code> f = (\x -> ... x ...) </code>の[[糖衣構文]]であるとも言える。このため、{{lang|en|Haskell}} の定義は変数に束縛するのが定数であるか関数であるかにかかわらず、「変数 = 値」という一貫した形でも定義できる。<br />
<br />
カリー化によって、{{lang|en|Haskell}} のあらゆる関数は引数を部分適用することができる。つまり、関数の引数の一部だけを渡すことで、一部の変数だけが適用された別の関数を作り出すことができる。また、{{lang|en|Haskell}} では演算子を部分適用することすら可能であり、演算子の部分適用をとくにセクション<ref>{{lang-en-short|section}}</ref>と呼ぶ。<br />
<br />
任意のリストをとり、その要素から条件にあう要素のみを取り出す関数 <code>filter</code> が標準ライブラリに定義されている。<br />
<source lang="haskell"><br />
filter :: (a -> Bool) -> [a] -> [a]<br />
</source><br />
この関数では第一引数に残す要素を判定する関数をとるが、このときに部分適用を使えばそのような関数を簡潔に書くことができる。整数のリストから正の値のみを取り出す関数 <code>positives</code> は次のように定義できる。<br />
<source lang="haskell"><br />
positives :: [Int] -> [Int]<br />
positives = filter (> 0)<br />
<br />
ps = positives [-4, 5, 0, 3, -1, 9] -- [5, 3, 9]<br />
</source><br />
<br />
ここで、<code>positives</code> および <code>filter</code> の第2引数はソースコード上に現れずに記述できているが、このように単純に書けるのもカリー化の恩恵によるものである。<br />
<br />
=== パターンマッチ ===<br />
{{lang|en|Haskell}} では関数の引数を様々な形で受け取ることができる。<br />
<br />
次は整数の要素をもつリストの全要素の合計を返す関数 <code>total</code> である。<br />
<source lang="haskell"><br />
total :: [Int] -> Int<br />
total [] = 0<br />
total (x:xs) = x + total xs<br />
</source><br />
<code>total</code> の関数本体の定義がふたつあるが、このうち引数の実行時の値と適合する本体が選択され呼び出される。上の本体定義では、引数が空のリストであるときのみ適合し、呼び出される。下の本体定義では、引数が少なくともひとつの要素を持つとき適合し、<code>x</code> に先頭の要素が束縛され、<code>xs</code> に残りのリストが束縛される。この例の場合はパターンに漏れはないが、もし適合するパターンが見つからない場合はエラーになる。<br />
<br />
複数の返り値を扱うのもタプルなどを利用して極めて簡明に書くことができる。<br />
<source lang="haskell"><br />
(x, y) = (10, 20)<br />
</source><br />
このとき、定義される変数は <code>x</code> および <code>y</code> で、それぞれ <code>x</code> は <code>10</code> に、<code>y</code> は <code>20</code> に定義される。<br />
<br />
=== リストとリスト内包表記 ===<br />
{{lang|en|Haskell}} で順序付けられた複数の値を扱うのにもっとも柔軟で簡潔な方法は[[リスト (抽象データ型)|リスト]]を用いることである。次は四季の名前のリストである。<br />
<source lang="haskell"><br />
["Spring", "Summer", "Autumn", "Winter"]<br />
</source><br />
次は初項10、公差4の等差数列のリストである。このリストは無限リストであり、その長さは無限大である。<br />
<source lang="haskell"><br />
[10, 14..]<br />
</source><br />
次の式は先ほどの数列の先頭20項を要素に持つリストである。<code>take n l</code> はリスト <code>l</code> の先頭 <code>n</code> 個の項を要素に持つリストを返す関数である。<br />
<source lang="haskell"><br />
take 20 [10, 14..]<br />
</source><br />
もし正格な動作を持つ言語でこのような定義をしようとすると関数 <code>take</code> に値を渡す前に無限リストを生成することになるが、長さが無限のため無限リストの生成が終わることはなく関数 <code>take</code> がいつまでも呼び出されなくなってしまう。{{lang|en|Haskell}} は遅延評価されるため、このようなリストを定義しても必要になるまで必要な項の値の生成は遅延される。このように無限リストを扱えるのは {{lang|en|Haskell}} の大きな強みである。次は[[素数]]列をリスト内包表記を用いて定義した一例である。<br />
<source lang="haskell"><br />
primes :: [Int]<br />
primes = [x | x <- [2..], and [rem x y /= 0 | y <- [2 .. x - 1]]]<br />
</source><br />
リストはその柔軟性から再帰的な関数での値の受け渡しに向いているが、任意の位置の要素にアクセスするためには参照を先頭からたどる必要があるのでランダムアクセスが遅い、要素を変更するたびにリストの一部を作り直さなければならないなどの欠点がある。このため {{lang|en|Haskell}} にも[[配列]]が用意されており、高速な参照や更新が必要なプログラムではリストの代わりに配列を用いることでパフォーマンスを改善できる可能性がある。<br />
<br />
=== 型クラスとインスタンス ===<br />
<!-- 例としてやや複雑すぎるか? --><br />
型クラス<ref>{{lang-en-short|type class}}</ref>は相異なるデータ型に共通したインターフェイスを持つ関数を定義する。例えば、順序づけることができる要素をもつリストを[[ソート]]できる関数 <code>sort</code> を定義することを考える。リストの要素のデータ型は関数 <code>sort</code> を定義するときには不明であり、その要素をどのように順序付けるかを予め決定しておくことはできない。数値型も文字列型もそれぞれのデータを順序付けることができるであろうが、共通してデータの順序を返す抽象的な関数 <code>order</code> を定義することができれば、それを用いてソートすることができる。<br />
<br />
まず型クラス <code>Comparer</code> を定義して、順序付ける関数 <code>order</code> の形式を定義する。値の順序を調べる関数 <code>order x y</code> は <code>x</code> → <code>y</code> が昇順のときは負の値、降順の時は正の値、<code>x</code> と <code>y</code> が等しいときは <code>0</code> を返すものとする。ここで <code>class</code> は型クラスの宣言であることを示す予約語である。<br />
<source lang="haskell"><br />
class Comparer a where<br />
order :: a -> a -> Int<br />
</source><br />
型クラスを実装するには、対象のデータ型に対してインスタンス宣言を行う。次は型 <code>a</code> の型クラス <code>Comparer</code> に対するインスタンスを宣言したものである。このインスタンス宣言により、<code>Enum</code> のインスタンスである任意の型のリストを辞書順に比較できる。例えば、文字列 <code>Char</code> は <code>Enum</code> のインスタンスの <code>Char</code> のリストであり、このインスタンス定義により <code>order</code> を適用することができるようになる。ここで関数 <code>fromEnum c</code> は文字 <code>c</code> を数値に変換する関数である。<code>Comparer</code> のインスタンスを定義する型 <code>a</code> を <code>Eq</code> および <code>Enum</code> のインスタンスを持つものに限定しているので(<code>(Eq a, Enum a) =&gt;</code> の部分)、インスタンス定義の内部で <code>Eq</code> の関数である <code>(/=)</code> や <code>Enum</code> の関数である <code>fromEnum</code> を使うことができている。<br />
<source lang="haskell"><br />
instance (Eq a, Enum a) => Comparer [a] where<br />
order [] [] = 0<br />
order _ [] = 1<br />
order [] _ = -1<br />
order (x:xs) (y:ys) | x /= y = fromEnum x - fromEnum y <br />
| otherwise = order xs ys<br />
</source><br />
次にリストをクイックソートする関数 <code>sort</code> を示す。型クラスを用いて順序付ける関数 <code>order</code> を抽象化したため、このように型クラス <code>Comparer</code> のインスタンスを持つ全ての型の値に適用できる一般化されたソート関数を定義できるのである。<br />
<source lang="haskell"><br />
sort :: Comparer a => [a] -> [a]<br />
sort [] = []<br />
sort (x:xs) = sort [e | e <- xs, order e x < 0] ++ [x] ++ sort [e | e <- xs, order e x >= 0]<br />
</source><br />
この関数 <code>sort</code> は次のように使う。<br />
<source lang="haskell"><br />
main = do<br />
print (sort ["foo", "bar", "baz"]) -- ["bar", "baz", "foo"] と出力される。<br />
print (sort [[5, 9], [1, 2], [5, 6]]) -- [[1, 2], [5, 6], [5, 9]] と出力される。<br />
</source><br />
{{lang|en|Haskell}} のインスタンス宣言は複数の型に共通する操作を定義するという点で {{lang|en|Java}} や {{lang|en|C#}} の「インターフェイス」と似ているが、{{lang|en|Haskell}} では既存の任意の型についてもインスタンスを定義できる点でもインターフェイスに比べて柔軟である。<br />
<br />
=== 入出力 ===<br />
すべての式が[[参照透過性|参照透過]]である {{lang|en|Haskell}} においては、[[副作用 (プログラム)|副作用]]を式の評価そのものでは表現できない。そのため、{{lang|en|Haskell}} では[[圏論]]のアイデアを利用した[[モナド (プログラミング)|モナド]]によって入出力が表現されており、{{lang|en|Haskell}} でも最も特徴的な部分となっている。次は {{lang|en|Haskell}} による {{lang|en|[[Hello world]]}} の一例である。実際に単独で実行可能形式にコンパイルできる、小さいが完全なプログラムの一例にもなっている。<br />
<source lang="haskell"><br />
main :: IO ()<br />
main = putStrLn "Hello,World!"<br />
</source><br />
{{lang|en|Haskell}} は純粋関数型言語であり、<code>main</code> もやはり参照透過である(副作用はない)。しかし処理系は <code>main</code> として定義された <code>IO ()</code> 型の値をそのプログラムの動作を示す値として特別に扱う。<code>putStrLn</code> は標準出力に文字列を出力する動作を表す <code>IO ()</code> 型の値を返す関数であり、実行すると引数として渡された <code>Hello,World!</code> を出力する。次に標準入力から一行読み込み、そのまま標準出力に出力する[[エコー (コンピュータ)|エコープログラム]]を考える。次のような、C言語などのような表記はできない。<code>getLine</code> 関数は標準入力から一行読み取る関数である。<br />
<source lang="haskell"><br />
main = putStrLn getLine --コンパイルエラー!<br />
</source><br />
<code>getLine</code> と <code>putStrLn</code> はそれぞれ次のような型を持っている。<br />
<source lang="haskell"><br />
getLine :: IO String<br />
<br />
putStrLn :: String -> IO ()<br />
</source><br />
純粋関数型である {{lang|en|Haskell}} では <code>getLine</code> もやはり副作用はなく、<code>getLine</code> は一行読み込むという動作を表す値を常に返す。このように入出力の動作を表す値をアクション<ref>{{lang-en-short|action}}</ref>と呼ぶ。<code>getLine</code> が返すのは <code>String</code> そのものではなくあくまで <code>IO String</code> という型を持ったアクションであって、それを <code>putStrLn</code> の引数に与えることはできない。正しいエコープログラムの一例は次のようになる。<br />
<source lang="haskell"><br />
main :: IO ()<br />
main = getLine >>= putStrLn<br />
</source><br />
ここでは演算子 <code>&gt;&gt;=</code> によってふたつのアクションを連結している。このとき、<code>main</code> は一行読み込みそれを出力するというアクションとなり、このプログラムを実行すると処理系は <code>main</code> が持つアクションの内容を実行する。このアクションを実行すると、<code>getLine</code> は一行読み込み、それを <code>putStrLn</code> に渡す。このとき、読み込まれたデータにこれらのアクションの外側からアクセスすることはできない。このため、この式は参照透過性を保つことができる。<ref>GHC には <code>System.IO.Unsafe</code> モジュールに <code>unsafePerformIO</code> という関数があり、副作用を持ちながらアクションでない型を返すことができる。これは参照透過性に対する[[バックドア]]であり、{{lang|en|Haskell}} の参照透過性を破壊する恐れがあるので、注意深く使わなければならない。</ref><br />
<br />
モナドは、Freeモナド、Operationalモナドと呼ばれる構造により、より単純なデータ型から導出することもできる。これにより、非常に強力な[[依存性の注入]]が実現できる。<br />
<br />
== 実例 ==<br />
以下の単純な例は関数型言語としての構文の実例にしばしば用いられるもので、{{lang|en|Haskell}} で示された[[階乗]]関数である。<br />
<source lang="haskell"><br />
fac :: Integer -> Integer<br />
fac 0 = 1<br />
fac n | n > 0 = n * fac (n-1)<br />
</source><br />
階乗を単一の条件による終端を伴う再帰的関数として表現している。これは[[数学]]の[[教科書]]でみられる階乗の表現に似ている。{{lang|en|Haskell}} コードの大半は、その簡潔さと構文において基本的な数学的記法と似通っている。<br />
<br />
この階乗関数の1行目は関数の型を示すもので、省略可能である。これは「関数 <code>fac</code> は <code>Integer</code> から <code>Integer</code> へ(<code>Integer -> Integer</code>)の型を持つ(<code>::</code>)」と読める。これは整数を引数としてとり、別の整数を返す。もしプログラマが型注釈を与えない場合、この定義の型は自動的に推測される。<br />
<br />
2行目では {{lang|en|Haskell}} の重要な機能であるパターンマッチングに頼っている。<!-- 関数のパラメータが丸括弧で囲まれておらず、しかし[[スペース|空白]]で区切られていることに注意されたい。-->関数の[[引数]]が <code>0</code>であれば、これは <code>1</code> を返す。その他の場合は3行目が試される。これは再帰的な呼び出しで、nが0に達するまで繰り返し関数が実行される。<br />
<br />
ガードは階乗が定義されない[[正の数と負の数|負]]の値から3行目を保護している。このガードが無ければこの関数は0の終端条件に達することなく、再帰してすべての負の値を経由してしまう。実際、このパターンマッチングは完全ではない。もし関数facに負の整数が引数として渡されると、このプログラムは実行時[[エラー]]とともに落ちるであろう。<code>fac _ = error "negative value"</code>のように定義を追加すれば適切な[[エラーメッセージ]]を出力することができる。<!--<br />
<br />
「<code>Prelude</code>」は基本[[ライブラリ]]であり、小規模な関数群を提供する。<code>Prelude</code> を用いて変数を利用しないポイントフリースタイルで書くと、先ほどの関数は次のようになる。<br />
<br />
<source language="haskell">fac = product . enumFromTo 1</source><br />
<br />
完全な {{lang|en|Haskell}} プログラムを書いて[[コンパイル]]せずに、{{lang|en|Hugs}} インタプリタでこのような[[ソフトウェアテスト|テスト]]を簡単に行う方法は、「<code>where</code>」節を用いることである。関数名とパラメータに続けて <code>where</code> とこの関数定義を入力する。<br />
<br />
<source language="haskell">fac 5 where fac = product . enumFromTo 1</source><br />
<br />
上記は <math>f = g \circ h</math>(関数合成を参照)のような数学的な定義に近く、[[変数]]への値の割り当てとはまったく異なる。--><br />
<br />
=== より複合的な例 ===<br />
引数 <code>f</code> を伴う[[高階関数]]で表現された単純な[[逆ポーランド記法]]評価器が、[[パターンマッチング]]と型クラス <code>Read</code> を用いた <code>where</code> 節で定義されている。<br />
<source lang="haskell"><br />
calc :: String -> [Float]<br />
calc = foldl f [] . words<br />
where <br />
f (x:y:zs) "+" = y+x:zs<br />
f (x:y:zs) "-" = y-x:zs<br />
f (x:y:zs) "*" = y*x:zs<br />
f (x:y:zs) "/" = y/x:zs<br />
f xs y = read y : xs<br />
</source><br />
空リストを初期状態とし、<code>f</code> を使って一語ずつ文字列を解釈していく。<code>f</code> は、注目している語が演算子ならばその演算を実行し、それ以外ならば浮動小数点として計算スタックに積んでいる。<br />
<br />
次は[[フィボナッチ数列]]のリストである。ある値nに対するfib(n)は一回しか計算しないようになっており、その点ではナイーブなフィボナッチと異なる効率の良いコードとなっている。<br />
<source lang="haskell"><br />
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)<br />
</source><br />
[[無限]]リストは 余再帰<ref>{{lang-en-short|corecursion}}</ref>(リストの後ろの値は要求があったときに <code>0</code> と <code>1</code> の二つの初期要素から開始され算出される)によって実現される。このような定義は遅延評価の実例で、{{lang|en|Haskell}} プログラミングでも重要な部分である。<br />
<br />
ただし、フィボナッチ数列の生成の場合は、ある要素の値が必要であれば、その要素より前にある要素の値は全て必要である。従って、遅延させた上で結局は後から全てその値を求めることになり、むしろ無駄であるので、この場合は遅延させない組込関数seqを使用したほうが効率は良くなり<ref>[http://d.hatena.ne.jp/kazu-yamamoto/20100624/1277348961 Haskellの神話 - あどけない話]</ref>、fib 1000000 といったような値を計算するような場合には差が見えてくる。あるいはリストの先に出る値から順番に計算させるとそのほうが速い、といったことが起きる。(さらに、フィボナッチ数はnに対して2<sup>n</sup>と大きな値になるので、そのようなBigIntegerの加算のコストも掛かり、以前にこの場所に書かれていたような「線形時間でのフィボナッチ数列の生成」は、大きい値では不可能である)<br />
<br />
どのように評価が進行するかの例示のために、次は6つの要素の計算の後の <code>fibs</code> と <code>tail fibs</code> の値と、どのように<code>zipWith (+)</code> が4つの要素を生成し、次の値を生成し続けるかを図示したものである。<br />
<source lang="haskell"><br />
fibs = 0 : 1 : 1 : 2 : 3 : 5 : ...<br />
+ + + + + +<br />
tail fibs = 1 : 1 : 2 : 3 : 5 : ...<br />
= = = = = =<br />
zipWith ... = 1 : 2 : 3 : 5 : 8 : ...<br />
fibs = 0 : 1 : 1 : 2 : 3 : 5 : 8 : ...<br />
</source><br />
次はGHCで使える言語拡張で、リスト内包表記を並列的な生成を記述できるよう拡張するParallelListCompを用いて書いた同様の式である。(GHCの拡張は特別な[[コマンドライン]][[フラグ (コンピュータ)|フラグ]]、またはLANGUAGEプラグマによって有効にしなければならない。詳しくはGHCのマニュアルを参照のこと)<br />
<source lang="haskell"><br />
{-# LANGUAGE ParallelListComp #-}<br />
fibs = 0 : 1 : [ a+b | a <- fibs | b <- tail fibs ]<br />
</source><br />
先に見た階乗の定義は、次のように関数の列を内包表記で生成して書く事もできる。<br />
<source lang="haskell"><br />
fac n = foldl (.) id [(*k) | k <- [1..n]] 1<br />
</source><br />
特に簡潔に書ける例として、ハミング数が順番に並ぶリストを返す以下のような関数がある。<br />
<source lang="haskell"><br />
hamming = 1 : map (*2) hamming # map (*3) hamming # map (*5) hamming<br />
where xxs@(x:xs) # yys@(y:ys)<br />
| x==y = x : xs # ys<br />
| x<y = x : xs # yys<br />
| x>y = y : xxs # ys<br />
</source><br />
上に示されたさまざまな <code>fibs</code> の定義のように、これはオンデマンドに数のリストを実現するために無限リストを使っており、<code>1</code> という先頭要素から後続の要素を生成している。<br />
<br />
ここでは、後続要素の構築関数は <code>where</code> 節内の記号 <code>#</code> で表現された中置演算子で定義されている。<!-- 適用の構文は異なるが、演算子は識別子が記号からなる関数である。--><br />
<br />
各|は、[[等号]]の前半の条件部分と、後半の定義部分からなるガード節を構成する。<br />
<br />
== ライブラリ ==<br />
=== parsec ===<br />
parsec は {{lang|en|Haskell}} で書かれたパーサコンビネータである。[[パーサ]]の一種であるが[[コンパイラコンパイラ]]とは異なり、<code>Parsec</code> はパーサのソースコードを出力するのではなく、純粋に {{lang|en|Haskell}} の関数としてパーサを構成する。{{lang|en|[[yacc]]}} のようにプログラミング言語と異なる言語を新たに習得する必要がなく、高速でかつ堅牢で、型安全で、演算子の結合性や優先順位を考慮したパーサを自動的に構成したり、動的にパーサを変更することすらできる。<br />
<br />
派生として、大幅に高速なパースが可能な'''attoparsec'''<ref>http://hackage.haskell.org/package/attoparsec</ref>、高機能でより洗練された'''trifecta'''<ref>http://hackage.haskell.org/package/trifecta</ref>がある。<br />
<br />
==={{lang|en|Template Haskell}}===<br />
{{lang|en|Template Haskell}} は {{lang|en|Haskell}} の[[メタプログラミング]]を可能にする拡張である。{{lang|en|Haskell}} ソースコードの構文木を直接 {{lang|en|Haskell}} から操作することができ、C、{{lang|en|C++}} のマクロやテンプレートを上回る極めて柔軟なプログラミングを行うことができる。<br />
<br />
=== QuickCheck ===<br />
<code>[[QuickCheck]]</code> はデータ駆動型のテストを行うモジュールである。自動的にテストデータを作成してプログラムの正当性をテストすることができる。<br />
<br />
=== lens ===<br />
lensはアクセサの概念を一般化するライブラリであり、様々なデータ構造に対して共通のインタフェースを与える。Getter、Setterは一つの関数として表現されており、それらを合成することもできる。[[JavaScript Object Notation|JSON]]や[[Extensible Markup Language|XML]]の処理にも応用できる。<br />
<br />
== 批判 ==<br />
{{lang|en|Haskell}} は他の[[プログラミング言語]]には見られない多くの先進的機能を持っているが、これらの機能のいくつかは言語を複雑にしすぎており、理解が困難であると批判されてきた。とりわけ、関数型プログラミング言語と主流でないプログラミング言語に対する批判は {{lang|en|Haskell}} にもあてはまる。加えて、{{lang|en|Haskell}} の潔癖さとその理論中心の起源に起因する不満がある。<br />
<br />
[[2002年]]に Jan-Willem Maessen、[[2003年]]に Simon Peyton Jones が遅延評価に関連するこの問題を議論し、その上でこの理論的動機を認識した。彼らは実行時のオーバーヘッドの悪化に加え、遅延はコードのパフォーマンスの推察をより困難にすると言及した。<br />
<br />
2003年に Bastiaan Heeren と Daan Leijen、Arjan van IJzendoorn は {{lang|en|Haskell}} の学習者にとってのつまづきに気がついた。これを解決するために、彼らは {{lang|en|Helium}} と呼ばれる新たなインタプリタを開発し、この中でいくつかの型クラスのサポートを取り除き、{{lang|en|Haskell}} の機能の一般化を制限することによってエラーメッセージのユーザ親和性を改善した。<br />
<br />
== 実装 ==<br />
以下は {{lang|en|Haskell}} 98 仕様を完全に満たす、または仕様に非常に近く、[[オープンソース]]ライセンスの下で配布されているものである。ここには現在のところ商用のHaskell実装は含まれない。<br />
<br />
;{{lang|en|Glasgow Haskell Compiler}}<br />
:{{lang|en|The Glasgow Haskell Compiler}} は異なる複数の[[コンピュータ・アーキテクチャ|アーキテクチャ]]のネイティブコードにコンパイルできるほか、C言語にコンパイルすることもできる。おそらくGHCは最も知られた {{lang|en|Haskell}} コンパイラで、現在のところGHCのみで動く言語拡張が実装されており、かなり便利ないくつかのライブラリはGHCのみで動作する(たとえは {{lang|en|[[OpenGL]]}} の[[言語バインディング|バインディング]]など)。http://www.haskell.org/ghc/<br />
;{{lang|en|Gofer}}<br />
:初期(Haskell 1.2)の仕様をベースとした、等式推論に向いた<ref>{{lang-en-short|good for equational reasoning}}</ref>方言およびインタプリタの実装。当時の {{lang|en|Haskell}} より {{lang|en|Miranda}} に似た構文、標準の機能をいくつか満たさない、逆に標準にないいくつかの機能の追加、などの特徴があった。マーク・ジョーンズにより開発された。その役割の多くは {{lang|en|Hugs}} に譲られた。{{lang|en|Hugs}} の「{{lang|en|g}}」は {{lang|en|Gofer}} に由来する。<br />
;HBC<br />
:これは別のネイティブコード {{lang|en|Haskell}} コンパイラ。これはしばらくの間開発が活発ではなくなっているが、未だに利用可能である。http://www.cs.chalmers.se/~augustss/hbc/hbc.html<br />
;{{lang|en|Helium}}<br />
:これは新しい {{lang|en|Haskell}} の方言である。開発の際の焦点は、明瞭なエラーメッセージを提供し学習を容易にすることである。Heliumは型クラスをサポートせず、多くのHaskellプログラムとは互換性のない実装である。http://foswiki.cs.uu.nl/foswiki/Helium<br />
;{{lang|en|Hugs}}<br />
:これはバイトコードインタプリタである。これは速いプログラム編集とそれなりの実行スピードを提供する。これは単純なグラフィックスライブラリを含む。多くの人が {{lang|en|Haskell}} の基本を学ぶのによいが、これはオモチャ的な実装であるということではない。これは最も可搬で軽量な {{lang|en|Haskell}} 実装である。http://www.haskell.org/hugs/<br />
;jhc<br />
:これは新しいプログラム変換の研究用としてのみならず、スピードと生成されるプログラムの効率を重視した John Meacham によって書かれた {{lang|en|Haskell}} コンパイラである。http://repetae.net/john/computer/jhc/<br />
;nhc98<br />
:これは別のバイトコードコンパイラであるが、そのバイトコードは {{lang|en|Hugs}} より顕著に速く走る。Nhc98は省メモリ使用に焦点を絞っており、古いマシンや遅いマシンにはとりわけよい選択肢となる。http://www.cs.york.ac.uk/fp/nhc98/<br />
;yhc<br />
:これはnhc98の派生物で、単純かつ可搬で効率的、{{lang|en|integrating Hat}} のサポートを目標とする。http://haskell.org/haskellwiki/Yhc<br />
<br />
== 代表的なアプリケーション ==<br />
* {{仮リンク|Darcs|en|Darcs}} - 分散[[バージョン管理システム]]。<br />
* Monadius - [[グラディウス (ゲーム)|グラディウス]]・クローンのシューティングゲーム。<br />
* [[Pandoc]] - [[Markdown]]などを変換するドキュメント・コンバータ。<br />
* [[Pugs]] - Perl6コンパイラ・インタプリタ。<br />
* [[xmonad]] - [[X Window System]]の[[ウィンドウマネージャ]]。<br />
* {{仮リンク|Yesod|en|Yesod (web framework)}} - [[Webアプリケーションフレームワーク]]。<br />
<br />
== 脚注 ==<br />
{{reflist}}<br />
<br />
== 参考文献 ==<br />
*Simon Peyton Jones. [http://research.microsoft.com/~simonpj/papers/haskell-retrospective ''Wearing the hair shirt: a retrospective on Haskell''] 2003年の[[:en:POPL]]での招待演説。<br />
*Jan-Willem Maessen. ''Eager Haskell: Resource-bounded execution yields efficient iteration''. 2002年の[[Association for Computing Machinery|ACM]] SIGPLAN workshopでのHaskellの記録。<br />
*Bastiaan Heeren, Daan Leijen, Arjan van IJzendoorn. [http://www.cs.uu.nl/~bastiaan/heeren-helium.pdf ''Helium, for learning Haskell'']. 2003年の ACM SIGPLAN workshop でのHaskellの記録。<br />
<br />
== 関連項目 ==<br />
*[[Atom (ハードウェア記述言語)|Atom]] - Haskell言語ベースのハードウェア記述言語<br />
*[[Bluespec]] - Haskell言語ベースのハードウェア記述言語<br />
*[[Hydra]] - Haskell言語ベースのハードウェア記述言語<br />
*{{仮リンク|Idris|en|Idris}} - Haskellに似た構文を持つ、[[依存型]]を持つ、正格な純粋関数型言語<br />
*[[Elm (プログラミング言語)|Elm]] - JavaScriptのコードを生成するリアクティブなプログラミング言語<br />
*[[Fay]] - JavaScriptのコードを生成するHaskellのサブセット<br />
<!-- *[[O'Haskell]]([[:en:O'Haskell|en]])はオブジェクト指向と並列プログラミングを提供するHaskellの拡張である。--><br />
* [[オフサイドルール]]<br />
<br />
== 外部リンク ==<br />
*[http://haskell.org/ HaskellWiki] - Haskellの本家。<br />
**[http://haskell.org/hawiki/ Old HaWiki] - Haskellのいくつかの古い話題の議論<br />
**[http://haskell.org/haskellwiki/Humor Haskell Humor]<br />
**[http://www.haskell.org/~pairwise/intro/intro.html Haskell Tutorial for C Programmers] by Eric Etheridge<br />
**[http://haskell.org/tutorial/ A Gentle Introduction to Haskell 98] - ([http://www.haskell.org/tutorial/haskell-98-tutorial.pdf [[Portable Document Format|PDF]][[ファイル (コンピュータ)|ファイル]]としても入手可能])<br />
**[http://haskell.cs.yale.edu/wp-content/uploads/2011/03/HaskellVsAda-NSWC.pdf Haskell vs. Ada vs. C++ vs. Awk vs. ... An Experiment in Software Prototyping Productivity] - ([[PDF]]ファイル)<br />
*[http://www.umiacs.umd.edu/~hal/docs/daume02yaht.pdf Yet Another Haskell Tutorial] - Hal Daume IIIによるよいHaskellチュートリアル。公式チュートリアルに先立つより厳選された知識。<br />
*[http://www.willamette.edu/~fruehr/haskell/evolution.html The Evolution of a Haskell Programmer] - Haskellで使える異なるプログラミングスタイルのちょっとユーモラスな概説。<br />
*[http://haskell.readscheme.org An Online Bibliography of Haskell Research]<br />
<br />
{{プログラミング言語一覧}}<br />
{{Computer-stub}}<br />
<br />
{{DEFAULTSORT:HASKELL}}<br />
[[Category:プログラミング言語]]<br />
[[Category:関数型言語]]<br />
[[Category:エポニム]]</div>
223.219.238.203
Warning : Cannot modify header information - headers already sent by (output started at /home/users/1/sub.jp-asate/web/wiki/extensions/HeadScript/HeadScript.php:3) in /home/users/1/sub.jp-asate/web/wiki/includes/WebResponse.php on line 46