web-dev-qa-db-ja.com

Haskellの大騒ぎは何ですか?

Haskellについて話し合っているプログラマーが何人かいることを知っていますが、ここではSO誰もがその言語を好むようです。

誰かがなぜそれがとてもエレガント/優れているのかを示すいくつかのHaskellの例を与えることができますか?

107
Frank

私に投げかけられた方法は、Haskellで1か月学習した後、私が本当だと思うことは、関数型プログラミングがあなたの脳を興味深い方法でひねるという事実です。 :ループの代わりに、マップ、フォールド、フィルターなどを検討します。一般に、問題について複数の観点がある場合、この問題について推論し、必要に応じて視点を切り替えることができます。

Haskellのもう1つの素晴らしい点は、その型システムです。厳密に型指定されていますが、型推論エンジンにより、愚かな型関連の間違いを行ったときに魔法のように通知するPythonプログラムのように感じられます。この点に関するHaskellのエラーメッセージはやや欠けています、しかし、あなたが言語に精通するにつれて、あなたは自分自身に言うでしょう:これがタイピングのはずです!

131
Edward Z. Yang

これはtheで、Haskellを学ぶように私を説得した例です(そして、少年は私がやったことがうれしいです).

-- program to copy a file --
import System.Environment

main = do
         --read command-line arguments
         [file1, file2] <- getArgs

         --copy file contents
         str <- readFile file1
         writeFile file2 str

OK、それは短く、読みやすいプログラムです。その意味では、Cプログラムよりも優れています。しかし、これは(=)Python非常によく似た構造のプログラムとはどう違うのでしょうか?

答えは遅延評価です。ほとんどの言語(機能的な言語を含む)で、上記のような構造のプログラムでは、ファイル全体がメモリにロードされ、新しい名前で再度書き込まれます。

Haskellは「怠laz」です。必要になるまで物事を計算することはありませんし、拡張によりdoes n't必要としない物事を計算します。たとえば、writeFile行を削除する場合、Haskellはそもそもファイルから何も読み取ろうとしません。

そのまま、HaskellはwriteFilereadFileに依存することを認識しているため、このデータパスを最適化できます。

結果はコンパイラに依存しますが、上記のプログラムを実行すると通常起こることは次のとおりです。プログラムは最初のファイルのブロック(たとえば8KB)を読み取り、次に2番目のファイルに書き込み、最初のファイルから別のブロックを読み取りますファイル、2番目のファイルなどに書き込みます。 (straceを実行してみてください!)

...これは、ファイルコピーの効率的なC実装が行うこととよく似ています。

したがって、Haskellを使用すると、多くのパフォーマンスを犠牲にすることなく、コンパクトで読み取り可能なプログラムを作成できます。

もう1つ追加する必要があるのは、Haskellがバグのあるプログラムの作成を単純に難しくしていることです。驚くべき型システム、副作用の欠如、そしてもちろんHaskellコードのコンパクトさは、少なくとも3つの理由でバグを減らします。

  1. プログラム設計の改善。複雑さが減ると、論理エラーが少なくなります。

  2. コンパクトなコード。バグが存在する行が少なくなります。

  3. コンパイルエラー。多くのバグがあります有効なHaskellではない

Haskellは万人向けではありません。しかし、みんな試してみてください。

135
Artelius

間違った質問をしているようなものです。

Haskellは、いくつかのクールな例を見て、「あー、今はthat's何が良いのか」と言う言語ではありません。

それは、他のすべてのプログラミング言語があり、それらはほぼ類似しているということです。そして、Haskellには、まったく違っていて奇抜な方法があります。しかし、問題は、奇抜に順応するのにかなり時間がかかることです。 Haskellを他のほぼ半主流の言語と区別するもの:

  • 遅延評価
  • 副作用なし(すべてが純粋で、IOなどがモナド経由で発生します)
  • 信じられないほど表現力豊かな静的型システム

他の多くの主流言語とは異なる(ただし一部の言語で共有されている)側面もあります。

  • 機能的
  • 重要な空白
  • 推測される型

他のポスターが答えたように、これらすべての機能の組み合わせは、プログラミングについてまったく異なる方法で考えることを意味します。したがって、これをJoe-mainstream-programmerに適切に伝える例(または例のセット)を思い付くのは困難です。それは体験的なものです。 (例証として、1970年の中国への旅行の写真をお見せすることができますが、写真を見た後は、その期間にそこに住んでいたことがどのようであったかはまだわかりません。同様に、私はHaskell 「クイックソート」ですが、Haskellerになることの意味はわかりません。)

65
Brian

Haskellを際立たせているのは、関数型プログラミングを実施するための設計への取り組みです。ほぼすべての言語で機能的なスタイルでプログラミングできますが、最初の都合で放棄するのは非常に簡単です。 Haskellは関数型プログラミングを放棄することを許可していないため、論理的な結論に至らなければなりません。論理的結論は、推論がより簡単で、最も厄介なタイプのバグのクラス全体を回避する最終プログラムです。

現実世界で使用するプログラムを作成する場合、Haskellには実用的な方法が欠けていることに気付くかもしれませんが、Haskellを知っている方が最終的な解決策は優れています。まだそこにいるわけではありませんが、Haskellの学習は、LISPが大学にいたと言うよりもはるかに啓発的です。

26
gtd

大騒ぎの一部は、純度と静的型付けにより、並列化と積極的な最適化が可能になることです。並列言語は現在、ホットであり、マルチコアは少し破壊的です。

Haskellは、高速でネイティブコードコンパイラとともに、ほとんどすべての汎用言語よりも多くの並列処理オプションを提供します。並列スタイルに対するこの種のサポートとの競合は実際にはありません。

したがって、マルチコアを動作させることに関心がある場合、Haskellには何か言いたいことがあります。開始するのに最適な場所は、Simon Peyton Jonesの Haskellでの並列および並行プログラミングに関するチュートリアル です。

22
Don Stewart

私は昨年Haskellを学び、その中でかなり大きく複雑なプロジェクトを書くのに費やしました。 (このプロジェクトは自動化されたオプション取引システムであり、取引アルゴリズムから低レベルの高速市場データの解析と処理まで、すべてがHaskellで行われます。)それはかなり簡潔で理解しやすいです(適切な背景)Javaバージョンよりも、非常に堅牢です。

おそらく私にとって最大の勝利は、モノイド、モナドなどのようなものを通る制御フローをモジュール化できることです。非常に簡単な例は、注文モノイドです。などの式で

c1 `mappend` c2 `mappend` c3

ここで、c1などは、LTEQ、またはGTc1を返します。EQを返すと、式が続行され、c2; c2LTまたはGTを返し、それが全体の値であり、c3が評価されない場合この種のものは、モナドメッセージジェネレーターやパーサーなど、さまざまな種類の状態を持ち歩いたり、さまざまな中止条件を持っている場合や、特定の呼び出しに対して中止が本当に意味があるかどうかを判断したい場合に、かなり洗練され複雑になります「これ以上処理しない」または「最後にエラーを返すが、処理を続行してさらにエラーメッセージを収集する」ことを意味します。

これはすべて学習に時間がかかり、おそらくかなりの努力が必要なものです。したがって、これらのテクニックをまだ知らない人にとって説得力のある議論をするのは難しいかもしれません。 All About Monads チュートリアルは、この一面の非常に印象的なデモンストレーションを提供すると思いますが、素材に慣れていない人がすでに最初に「手に入れる」とは思わないでしょう。 3回目でも、注意深く読んでください。

とにかく、Haskellには他にもたくさんの良いものがありますが、これはあまり複雑ではないので、あまり頻繁に言及されていない主要なものです。

18
Curt J. Sampson

ソフトウェアトランザクションメモリ は、並行性を処理するための非常に優れた方法です。これは、メッセージの受け渡しよりもはるかに柔軟であり、ミューテックスのようなデッドロックの傾向がありません。 GHC's STMの実装は最高の1つと考えられています。

17
bmdhacks

興味深い例については、次を参照してください。 http://en.literateprograms.org/Quicksort_(Haskell)

興味深いのは、さまざまな言語での実装を確認することです。

Haskellを他の関数型言語と同様に興味深いものにしているのは、プログラミングの方法について異なる考え方をしなければならないという事実です。たとえば、通常forループやwhileループは使用しませんが、再帰を使用します。

上記のように、Haskellおよびその他の関数型言語は、マルチコアで動作する並列処理および書き込みアプリケーションを備えています。

11
James Black

私はあなたに例をあげることができませんでした、私はOCamlの男ですが、私があなた自身のような状況にいるとき、好奇心が固まり、コンパイラ/インタプリタをダウンロードして試してみる必要があります。おそらく、特定の関数型言語の長所と短所について、はるかに多くのことを学ぶでしょう。

7
maxaposteriori

アルゴリズムや数学的問題を扱うときに非常にクールな点の1つは、Haskell固有の計算の遅延評価です。これは、厳密な機能的性質のためにのみ可能です。

たとえば、すべての素数を計算する場合は、次を使用できます。

primes = sieve [2..]
    where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]

結果は実際には無限のリストです。しかし、Haskellはそれを右から左に評価するので、リスト全体を必要とする何かをしようとしない限り、プログラムが無限に行き詰まることなくそれを使用できます。

foo = sum $ takeWhile (<100) primes

これは、100未満のすべての素数を合計します。これは、いくつかの理由で素晴らしいです。まず、すべての素数を生成する素数関数を1つ記述するだけで、素数を扱う準備がほぼ整います。オブジェクト指向プログラミング言語では、関数が返す前に計算する素数を関数に伝える方法、またはオブジェクトで無限リストの動作をエミュレートする方法が必要です。もう1つのことは、一般に、結果を評価する順序ではなく、計算したいものを表現するコードを書くことになります-代わりにコンパイラがそれを行います。

これは無限リストに対してのみ有用ではありません。実際、必要以上に評価する必要がないときはいつでもそれを知らずに使用されます。

7
waxwing

Haskellを誇示するためには、いくつかの小さな例を見るのが最善の方法ではないことを他の人に同意します。しかし、とにかくいくつかをあげます。 Euler Projectの問題18および67 に対する超高速ソリューションを次に示します。これは、三角形の頂点から頂点までの最大合計パスを見つけるように求めます。

bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
  where bu [bottom]     = bottom
        bu (row : base) = merge row $ bu base
        merge [] [_] = []
        merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)

LeshとMitzenmacherによる BubbleSearch アルゴリズムの完全で再利用可能な実装を次に示します。アーカイブストレージ用の大きなメディアファイルを無駄なくDVDにパックするために使用しました。

data BubbleResult i o = BubbleResult { bestResult :: o
                                     , result :: o
                                     , leftoverRandoms :: [Double]
                                     }
bubbleSearch :: (Ord result) =>
                ([a] -> result) ->       -- greedy search algorithm
                Double ->                -- probability
                [a] ->                   -- list of items to be searched
                [Double] ->              -- list of random numbers
                [BubbleResult a result]  -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
    where bubble order rs = BubbleResult answer answer rs : walk tries
            where answer = search order
                  tries  = perturbations p order rs
                  walk ((order, rs) : rest) =
                      if result > answer then bubble order rs
                      else BubbleResult answer result rs : walk rest
                    where result = search order

perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
    where xr' = perturb xs rs
          perturb :: [a] -> [Double] -> ([a], [Double])
          perturb xs rs = shift_all p [] xs rs

shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
  where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
        shift_one new' xs rs k = shift new' [] xs rs
          where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
                shift new' prev' (x:xs) (r:rs) 
                    | r <= p    = k (x:new') (prev' `revApp` xs) rs
                    | otherwise = shift new' (x:prev') xs rs
                revApp xs ys = foldl (flip (:)) ys xs

このコードは、ランダムな意味不明なコードのように見えます。しかし Mitzenmacherのブログエントリ を読んでアルゴリズムを理解している場合、検索対象について何も言わずにアルゴリズムをコードにパッケージ化できることに驚くでしょう。

あなたが求めたいくつかの例を挙げて、私はHaskellを評価するための最良の方法は、DVDパッカーを書くために必要なアイデアを与えてくれた論文を読むことだと言います: なぜ機能プログラミングが重要なのか John Hughes。この論文は実際にはHaskellより前のものですが、Haskellのような人々を作るアイデアのいくつかを見事に説明しています。

6
Norman Ramsey

私にとって、Haskellの魅力は、コンパイラ保証の正確性の約束です。それがコードの純粋な部分のためであっても。

私は多くの科学的シミュレーションコードを書いており、以前のコードにバグがあり、多くの現在の作業が無効になる可能性がある場合、soを何度も疑問に思いました。

5
rpg

特定のタスクについて、私はHaskellで非常に生産的であることがわかりました。

その理由は、簡潔な構文とテストの容易さです。

これは、関数宣言の構文は次のとおりです。

foo a = a + 5

これは、関数を定義することについて考えることができる最も簡単な方法です。

逆を書くと

inverseFoo a = a-5

ランダム入力の逆であることを確認するには、次のように記述します。

prop_IsInverse :: Double-> Bool
prop_IsInverse a = a ==(inverseFoo $ foo a)

そして、コマンドラインから呼び出す

jonny @ ubuntu:runhaskell quickCheck + names fooFileName.hs

入力をランダムに100回テストすることにより、ファイル内のすべてのプロパティが保持されていることを確認します。

Haskellがすべてに最適な言語だとは思いませんが、小さな関数を書いてテストすることに関しては、これ以上良いものは見ていません。プログラミングに数学的な要素がある場合、これは非常に重要です。

5

Haskellの型システムに頭を包むことができれば、それ自体がかなりの成果だと思います。

3
Dr. Watson

ループ構造はありません。多くの言語にはこの特性がありません。

2
Badri

関数型プログラミングはあなたの脳をひねり、異なる角度からプログラミングを見ると言った人たちに同意します。私はそれを愛好家としてしか使用していませんが、問題へのアプローチ方法を根本的に変えたと思います。 Haskellに触れることなく(そしてPythonでジェネレーターとリスト内包表記を使用することなく)LINQで同じくらい効果的になるとは思いません。

1
Jacob