Haskellに関する世界観です。
純粋な関数型言語。やると人生が変わるらしい。
Haskell入門 5ステップにサンプルコードがあります。以下に「初めてのHaskell並列プログラミング」を解説します。
まさに「こんなに美しい」。facとackとfibは再帰関数で、パターンマッチを使うことで引数が0の時は~とする、それ以外の時は再帰的に呼び出す、といった「数学的に美しいコード」を記述できる。またwhereを使うことで、変数を使用している場所の下で定義できる(上で使うにはletを使う)。また、それだけではなくこのコードは並列処理で実行される(`par`が並列処理を表す)。
また、doを使うことで連続してprintしたりできる。letではdoがなければ終端にinが必要。``で引数を囲むのは中置演算子として利用するため。原則、変数は代入し直すことができない(代入ではなく束縛という言葉を使う)。その他もろもろについてはHaskell 超入門 - Qiitaを参照のこと。
Haskellは強力なパターンマッチングと呼ばれる機能があり、引数の値の構造によって関数の処理内容をそれぞれ分けて定義することができる。
たとえば、関数hogeに0を与えた時と、0以外の数を与えた時の処理は、
hoge :: Int -> Int hoge 0 = 1 hoge n = n * hoge (n - 1)
のように別々に書くことができる。
また、ガードは引数の値の構造ではなく、条件式にマッチするような「性質」で関数の処理内容をそれぞれ分けることができる機能。
パターンマッチがswitch-case文に相当するなら、ガードはif ~ else if文に相当すると言える。
2023.08.14編集
僕は、Haskellのような関数型言語には、「関数か、変数か」という考え方があると思う。
普通、手続き型のプログラミング言語で言う関数は、特定の名前がついていて、コードブロックのどこかにあって、一度決まったら再度変更することはできず、どこからでも呼び出せる。
逆に、変数にも特定の名前がついているが、使用するよりも前に宣言されている必要があり、一度決まっても再度変更することができて、同じスコープの中からのみ呼び出せる。
僕は、関数型のプログラミング言語とは、こうした「関数と変数の違い」を無くし、「全てを関数=変数として使う」ということができる、ということではないかと思う。
特に、HaskellやElmのような関数型言語では、関数と同じように、変数はどこからでも、順序を関係なく呼び出すことができて、再度変更することはできない。
こうした「関数か、変数か」という部分の統一が分かると、関数型言語は良く分かってくると思う。
C言語などの手続き型言語では、変数の値を変えることができます。
たとえば、
x = 10
とした下の行で、
x = 20
とすることができます。
これはC言語のようなプログラミング言語では正しくても、数学としてはおかしな変数です。さっきまではxが10だったにもかかわらず、後になってxが20であるということに「再定義」されてしまっています。
Haskellでは、このような記述はできません。xが10であれば、xは永遠に10であり、コード全体でxが10であることは永久に変わらないのです。
別の考え方をすると、なんらかのデータがあるとして、このデータに破壊的作用を加えると、データを編集することができますが、そのデータの前の状態は失われてしまいます。
このような破壊的作用を、関数型言語では嫌います。
関数型言語では、データを破壊的に編集するのではなく、新しいデータのための新しい変数を作って、新しい変数がその新しい編集後のデータを表すようにします。
このようにすると、編集後のデータも作ることができ、編集前のデータはその値のまま、まったく破壊的作用を加えられることなく、同じ値のままで残ります。
これは、「上書き保存」をするのではなく、常に「名前を付けて新しいファイルに保存」を行うのに近いと思います。
このようにするメリットはいくらかあります。
まず、変数はコードの中で値を変更されないため、もしバグがあっても、「どこでその値が書き換えられているのか」ということを考える必要がなくなりますし、データは同じ値しか保持しないため、「データの値が今何になっているか」ということに気を使ってプログラミングする必要がなくなります。
また、コードの中のどこであっても、変数は同じ値を持ちます。純粋関数型言語には「参照透過性」という原則があり、同じ式と引数で関数を呼び出せば必ず同じ結果が返ります。このため、「プログラムを実際に実行しなくても、実行する前の時点で問題を見つけやすい」ということが言えます。実行時にはじめて値が決まるのではなく、コードを書いた時点で既にその関数の値は決まっています。このため、もし値が目的とするものと違えば、そこにバグがあるということが明確になり、プログラムの実行前にバグを見つけやすくなります。
そして、コードのどこであっても値が同じであるため、変数の定義をした後で参照をするという順番を守る必要がなくなります。同じ変数は同じ値をどのコードの部分であっても保ちます。なので、Haskellでは、変数を使う前に変数を定義するという決まりがなく、変数を使うのよりも後に変数を定義することができます。
Haskellのメリットとして、「悟りが得られる」というのが僕はあると思います。
Haskellをやると、人生が変わると言っている方も居ます。これはRustと同様に「美しいスタイルでプログラミングができる能力がつく」というのもありますが、僕が思うに、「悟りが得られる」のです。
Haskellは、プログラミング言語界の仏典のようなものです。特に昔の僕のような引き篭もりやニートが自分で思いつく多くのことは、Haskellが実現しています。数学的でもありながら、「プログラミングにおける覚者」となることができます。
自分の書いたブログ「わたしの名はフレイ」2020/09/15より。
また、本当にプログラミングを極めたいのであれば、むしろ、Haskellが良いと思う。
Haskellは最近少し英語のドキュメントを読んでいる僕だが、Pure functionalで、static typingで、immutableで、Lazyで、変数と関数は基本的にまったく同じものとして扱われ、関数は「first-class」。
変数は変更可能な値の入れ物ではなく、単純に値につける名前であり、immutableで不変であり、状態を破壊(変更されてしまう)することがない。
これは数学の数式における変数の概念と同じ。
また、同じことを自分で繰り返すことはなく、実行時ではなくコンパイル時にエラーが発覚するように、などといった設計姿勢を持つ、「神経質だが大切なことを教えてくれる言語」である。
多くの、たとえばPythonやRustやJavaScriptなどに見られる特徴は、Haskellでは既に実現されていたことが多く、たくさんの高度な概念や考え方があるため、習得と理解には困難を要するが、おそらく本当にマスターできたときに、本当の「悟り」を得ることができるかもしれない。
自分の書いたブログ「わたしの名はフレイ」2020/09/16より。
今日は、作業所で仕事がなかったので、英語の読み上げソフトでHaskellのチュートリアルを聴いていた。
通常、変数は以下のように上から下へと順序的に書いていく。
x = 30 y = x + 10
しかしながら、Haskellでは変数がimmutable(不変)であり、一度名前付けされた値がプログラムのどの位置であっても変わらないため、記述を逆にできる。
y = x + 10 x = 30
このプログラムを、じっと見てほしい。
そう、気付くことがある。
これはまさに、関数の引数定義と呼び出しと同じである。
たとえば、C言語では関数は以下のように宣言・定義する。
#include <stdio.h> int add(int x) { return x + 10; } int main() { int y; y = add(30); printf("%d\n", y); }
Haskellではこうなる。letを使う場合(GHCiから実行):
let x = 30 in x + 10
あるいは、関数を定義する場合(以下は全てファイルから実行):
add x = x + 10 main = print (add 30)
あるいは、単にCと逆向きに書く場合:
main = do { print y } -- doブロックで複数のアクションの実行が可能 y = x + 10 x = 30
doブロックは;でそれぞれのアクションを区切るか、レイアウトと言ってインデントを揃えることでPythonのようにきれいに書くこともできる。
そして、whereを使うことでこのようにも書ける:
add = x + 10 where x = 30 main = print (add)
そう、ここには、変数と関数の区別もなく、順序通りに宣言・定義されるという決まりもない。
まさに、プログラミングは関数である。
そして、関数は変数であり、言うとすれば「計算」である。
計算においては、関数もステートメントも変わらない。
これが、Haskellの本質ではないかと僕は思う。
自分の書いたブログ「わたしの名はフレイ」2020/09/16より。
公式にあるオンラインのWebブラウザ上でHaskellを実行できる、
Try Haskellで「help」を実行してチュートリアルを一通りやった。
リストとパターンマッチングはとても面白い機能だと感じた。
自分の書いたブログ「わたしの名はフレイ」2020/09/16より。
Haskellは、「同じ関数を同じ引数の呼び出しで行うと必ず同じ結果が返る」という、純粋関数型言語であり、変数はimmutable(不変)である。
しかしながら、変数が不変だと不便なことがある。
特に、IOなどのデータ処理をどうするのか、という問題である。
Haskellでは、「モナド」という圏論のアイデアを使った考え方を用いて、参照透過性を壊すことなくIOを実現している。
標準的によく使われるListや、Maybeなどもモナドで実装されている。
モナドの解説はネット上にあふれているのでそれを参照してほしい(僕はまだ理解できていない)。
PugsはPerl 6の言語処理系。Perl戯言を参照のこと。
2023.08.29
xmonadはタイル型ウィンドウマネージャ。タイル型WMを参照のこと。
2023.08.29
関数型プログラミングも参照のこと。
Haskellのチュートリアル。