Miranda
パラダイム | 関数型、宣言型 |
---|---|
登場時期 | 1985年 |
設計者 | David Turner |
開発者 | Research Software Ltd |
型付け | 強い静的型付け |
主な処理系 | Miranda |
影響を受けた言語 | KRC、ML、SASL |
影響を与えた言語 | Haskell |
Miranda は、遅延評価方式の純粋関数型プログラミング言語である。デビッド・ターナー(David Turner)が自身が以前に開発したプログラミング言語 SASL や KRC の後継として、ML や Hopeのコンセプトを一部利用して開発した。イギリスのリサーチ・ソフトウェア社(Research Software Ltd.)が販売しており、Miranda は同社の商標である。研究目的ではない商用を目指した最初の純粋関数型言語であった。
よくある例題を解くプログラムに関して言えば、Miranda のコードはAPLには敵わないものの、ほとんどの主流のプログラミング言語よりも簡単で短く表現でき、他の関数型言語と同様、信頼性の高いプログラムの開発が命令型言語に比べて短期間で可能になったという報告がある。
1985年に登場したが、UNIX系オペレーティングシステム向けにC言語で実装された処理系しかない。後発の Haskell は多くの面で Miranda に似ている。
概要
Miranda が遅延評価式の純粋関数型言語であるとは、副作用がなく、命令型プログラミング機能が存在しないことを意味する。Miranda のプログラム(スクリプトと呼ばれる)は方程式の集合であり、それによって各種数学関数や代数データ型を定義する。ここでは集合という言葉が重要である。方程式の記述順序は一般に重要ではなく、何かを定義する前に使っていても全く問題ない。
構文解析ではオフサイドルールによる字下げを利用しているため、括弧で文を囲む必要がなく、文と文の区切りも不要である。このような例は他にも Occam や Haskell、Python がある。
||
からその行の終わりまでがコメントとして扱われる。別のコメント記法は「文芸的プログラミング」的なもので、行の先頭に >
がない行は全てコメントとして扱われる。
Miranda の基本データ型は char
、num
、bool
である。文字列は char
のリストであり、num
は内部的には2種類の形式があって、自動的に変換される(多倍長整数が既定で使われ、必要に応じて浮動小数点数が使われる)。
タプルは様々な型のデータの羅列であり、Pascal系言語での構造体に似ている。以下のように括弧を使って書かれる。
this_employee = ("Folland, Mary", 10560, False, 35)
実際にはタプルよりも「リスト」がよく使われる。角括弧で表され、各要素はカンマで区切られる。全要素は同じ型でなければならない。
week_days = ["Mon","Tue","Wed","Thur","Fri"]
リストの連結は ++
、差分は --
、要素追加は :
、サイズ(要素数)は #
、インデックス指定は !
である。以下に使用例を挙げる。
days = week_days ++ ["Sat","Sun"]
days = "Nil":days
days!0
→ "Nil"
days = days -- ["Nil"]
#days
→ 7
その他にもリスト構築のショートカットがある。..
は要素が数列の場合に利用可能で、1 ずつ増加する数列以外も指定可能である。
fac n = product [1..n]
odd_sum = sum [1,3..100]
さらに強力なリスト構築機能として「リスト内包表記」(ZF記法とも呼ばれていた)があり、2種類の形式がある。式を項の列に適用する形式は以下のようになる。
squares = [ n * n | n <- [1..] ]
この意味は、n の自乗のリストで、n は全ての正の整数のリストから取られる。また、ある項がその1つ前の項の関数から得られる形式は以下のようになる。
powers_of_2 = [ n | n <- 1, 2*n .. ]
これは、2のべき乗のリストである。これらの例から分かるように、Miranda では無限のリストを表現可能で、最も単純なリストは全ての正の整数のリスト [1..]
である。
関数呼び出しは sin x
のように単に関数名と引数を並置する。
Miranda は他の純粋関数型言語と同様、関数を引数として他の関数に渡したり、関数の結果として関数を返したり、データ構造に要素として関数を含むといったことが可能である。また、カリー化が可能である。例えば次のようになる。
add a b = a + b
increment = add 1
この場合、increment
は自身の引数に 1 を加算する。実際、add 4 7
は2つの引数をとる関数 add
だが、これも実際には単一引数 7
に 4 を加算する関数とも解釈できる。
2つの引数をとる関数を中置演算子にすることができる。例えば、上記の add
関数では、$add
という項を +
演算子と同じように使うことができる。逆に2つの引数に作用する中置演算子を関数に変換することもできる。従って、次のようになる。
increment = (+) 1
これは、引数に 1 を足す関数を記述する一番単純な方法である。同様に
half = (/ 2)
reciprocal = (1 /)
といった単一引数関数を作成できる。この場合、インタプリタは除算のどちらの引数が関数の引数として供給されるのかを理解しており、引数を 2 で割る関数や 1 を引数で割る関数が得られる。
Miranda は強い型付けをする言語だが、構文上は明確な型の宣言を必要としない。関数の型が明示されない場合、インタプリタは引数の型や引数の使われ方から型推論を行う。例えばリスト逆転関数などでは、基本データ型(char
, num
, bool
)に加えて、引数の型を問題としない anything
型がある。
rev [] = []
rev (a:x) = rev x ++ [a]
これは、任意のデータ型のリストに適用可能である。これを明示的に関数型を宣言するなら、次のようになる。
rev :: [*] -> [*]
最後に、Miranda にはプログラムのモジュールを構成する機構があり、モジュール外から内部関数が見えないようにできる。
コード例
以下の Miranda のスクリプトは、数の集合について、全ての部分集合を求める関数である。
subsets [] = [[]]
subsets (x:xs) = [[x] ++ y | y <- ys] ++ ys
where ys = subsets xs
以下は文芸的な記法で、全ての素数のリストを与える関数 primes
を定義している。
> || The infinite list of all prime numbers, by the sieve of Eratosthenes. The list of potential prime numbers starts as all integers from 2 onwards; as each prime is returned, all the following numbers that can exactly be divided by it are filtered out of the list of candidates. > primes = sieve [2..] > sieve (p:x) = p : sieve [n | n <- x; n mod p ~= 0]