はじめに
Dekorationは関数型の特徴を持ったプログラミング言語です。
Dekorationは以下の特徴を持ちます。
-
第1級の関数と無名関数
-
第1級の継続
-
末尾再帰の最適化
-
LISPライクなマクロ
-
メッセージパッシング機構
-
ユーザ定義可能な中置構文
-
制御構造をCのように書ける構文糖衣
導入
Node.js
インストールは以下のようにします。
コマンドラインからの実行をしたいときには-gオプションをつけてインストールします。
$ npm install dekoration
使用方法
コマンドラインから
REPLを起動するにはdekorationコマンドを実行します。
$ dekoration
構文リファレンス
字句文法
Dekorationの字句はシンボル、数値、真偽値のリストからなります。
シンボル
シンボルは以下の文字を除く文字列が使用できます。
. , : ; ( ) { } [ ]
シンボルの文字列中に下記の文字が現れたときは中置記法として扱われるためそこで区切られます。
+ - * / $ ^ ! # % & @ < >
下記のシンボルの列は中置記法の演算子として使用できます。
+ - * / ^ ! % & @ < >
シンボルの先頭に`(バッククオート)がついたときはバッククオート記号を除くシンボルとして使用な文字全てがシンボルとして扱われます。
シンボルの先頭に'(単一引用符)がついたときは引用符を除いたシンボルとして使用な文字がクオートされたシンボル(文字列)として扱われます。
"(二重引用符)で囲まれた文字はクオートされた引用符(文字列)として扱われます。
シンボルとして有効な字句を以下に示します。
字句 | シンボル |
---|---|
symbol |
symbol |
++ |
++ |
`string= |
string= |
'string= |
q(`string=) |
"This is a string" |
クオートされたThis is a string |
数値
Dekorationでは倍精度の浮動小数点数を使用できます。
真偽値
真偽値は文字列true, falseで表します。
リスト
リストは以下の形式で記述します。
header(element, ...)
上記のリストは次のリストと同じです。
#[header, element, ...]
要素の区切りには,(カンマ), ;(セミコロン), ⇒を使用することができます。
リストの最後にはオプションで{ … }で囲んだ要素の並びを付与することができます。
下記のリストは、
if(a) { b; c }
次のリストと同じです。
if(a, begin(b, c))
{ … }内の要素の区切りは;(セミコロン)で区切る必要があります。
{ … }の次にさらにリスト要素を追加することができます。下記のリストは、
if(a) { b; c } elseif(c) { d }
次のリストと同じです。
if(a, begin(b, c), elseif(c, begin(d)))
変数参照
クオートされていないシンボルは変数への参照になります。
関数の呼び出し
add(1, 2)
リストの先頭に関数名、その後に引数を指定することで関数を呼び出せます。
配列にインデックスを適用することで、そのインデックスの要素を取得できます。
オブジェクトにプロパティ名を適用することで、そのプロパティを取得することができます。
文字列にインデックスを適用することで、そのインデックスの文字(1文字の文字列)を取得することができます。
クオート
q(#[1, 2, 3])
リストをqで始めることによりクオートします。
ブロック
begin(add(1, 2), add(3, 4))
構文をブロック化します。最後の値が返されます。
関数作成
lambda(x, y, begin(add(x, y)))
lambda(x, y) { add(x, y) }
lambdar(x, y; r; begin(add(x, y, r(0))))
lambdar(x, y; r) { add(x, y, r(0)) }
関数を作成します。最初に引数の並びを、最後に関数の本体を記述します。
残余引数を使用したいときはlambdarフォームを使用します。
条件分岐
if(eqv(x, 0); 1, 2)
if(eqv(x, 0)) { 1 } else { 2 }
if(eqv(x, 0)) { 1 } elseif(eqv(x, 1)) { 2 } else { 3 }
条件分岐です。最初の引数の値がfalseでないときは2番目の引数を、falseのときは3番目の引数を実行します。
elseは省略可能です。
elseifはifと同義語です。また、elseはbeginと同義語です。
変数定義
x:1
中置の:演算子で変数を定義します。
関数定義
function(func; x, y, begin(add(x, y)))
function(func; x, y) { add(x, y) }
functionr(func; x, y; r; begin(add(x, y, r(0))))
functionr(func; x, y; r) { add(x, y, r(0)) }
関数を作成します。最初に定義したい変数名を、その次に引数の並びを、最後に関数の本体を記述します。
残余引数を使用したいときはfunctionrフォームを使用します。
変数への値のセット
x:=1
中置の:=演算子で変数を定義します。
変数はすでに値が束縛されている必要があります。
ローカル変数の定義
let(
x => 1,
y => 2;
add(x, y));
let(
x => 1,
y => 2) {
add(x, y)
}
ローカル変数に値を束縛します。
最初に束縛したい変数名と値のペアを、最後に実行した本体を記述します。
ループ(named let)
loop(sum;
x => 10,
y => 0;
if(eqv(x, 0), y, sum(x - 1, x + y))
)
loop(sum;
x => 10,
y => 0
) {
if(eqv(x, 0)) {
y
} else {
sum(x - 1, x + y)
}
}
letの本体を変数の最初の値に束縛します。
これにより、再帰によるループを実現できます。
ローカル関数の定義
letrec(
sum => lambda(x, y; if(eqv(x, 0), y, sum(x - 1, x + y));
sum(10, 0)
)
letrec(
sum => lambda(x, y) {
if(eqv(x, 0)) {
y
} else {
sum(x - 1, x + y)
}
}
) {
sum(10, 0)
}
letと同じですが、ローカル変数に束縛できる関数内でローカル変数の値を参照できます。
準クオート
qq(obj1(string), obj2(obj3(uq(add(1, 2)), uqs(#[1, 2]))))
クオートと同じですが、uqが先頭に来るリストが現れたときはuqの値を評価します。
uqsがリスト内に現れたときはuqsの値を評価してリストに追加します。
delay
delay(x + y)
delayの属性で与えられたコードを遅延して実行します。
遅延したコードはforceで取り出します。
取り出された結果はメモ化されます。以下のコードは765を結果として返します。
> x:765
> promise:delay(x)
> force(promise)
765
> x:=961
> force(promise)
961
マクロ
defmacro(aMacro; a, b; add(a, b))
defmacro(aMacro; a, b) {
add(a, b)
}
defmacror(aMacro; a, b; r; add(a, b, r(0)))
defmacror(aMacro; a, b; r) {
add(a, b, r(0))
}
マクロのパターンにマッチした構文に対して最後の引数で評価された値で置き換えます。
置き換えられた後再度マクロの評価がされます。それにより再帰的なマクロも記述できます。
マクロの呼び出しは通常の構文と同様にマクロ名をリストの先頭にして呼び出します。
and
and(1, 2, 3)
1 && 2
andに与えられた引数が全てfalseでないときは最も右の値が戻ります。
1つでもfalseがあったときはfalseになります。
引数が空のときはtrueとなります。
or
or(false, 2, 3)
false || 2
orに与えられた引数に1つでもfalseでない値があったときはその値が戻ります。
全てfalseのときはfalseになります。
引数が空のときはfalseになります。
メッセージパッシング
message(
a => 765,
b => 346
)
文字列を与えてその結果がオブジェクトのキーに対応する値を返す関数を定義します。
messageを利用して簡易的なオブジェクト指向を実現することができます。
while
while(i < 5, begin(p(i), i++))
while(i < 5) {
p(i);
i++
}
最初の引数がfalseでない間2番目の引数を実行します。
結果は最後に2番目の引数を実行したときの結果になります。
for
for(i, 0, i < 5, i++, p(i))
for(i => 0; i < 5; i++) {
p(i)
}
最初の引数に2番目の引数を束縛し、3番目の引数がfalseでない間、5番目の引数を実行して最後に4番目の引数を実行します。
結果は最後に4番目の引数を実行したときの結果になります。
演算子
Dekorationであらかじめ定義されている演算子と優先順位は以下の通りです。
優先順位 | 演算子 | 結合性 | 説明 |
---|---|---|---|
2700 |
++(後置) |
- |
変数を1増やす |
--(後置) |
- |
変数を1減らす |
|
2600 |
++(前置) |
- |
変数を1増やす |
--(前置) |
- |
変数を1減らす |
|
+(単項) |
右から左 |
そのまま |
|
-(単項) |
右から左 |
符号を反転する |
|
! |
右から左 |
falseのときはtrueに、それ以外のときはfalseにする |
|
2400 |
* |
左から右 |
乗算 |
/ |
左から右 |
除算 |
|
2300 |
+ |
左から右 |
加算 |
- |
左から右 |
減算 |
|
2100 |
< |
左から右 |
数値の比較 |
> |
左から右 |
数値の比較 |
|
⇐ |
左から右 |
数値の比較 |
|
>= |
左から右 |
数値の比較 |
|
2000 |
= |
左から右 |
数値が等しい |
!= |
左から右 |
数値が等しくない |
|
=== |
左から右 |
値が等価(eqv) |
|
!== |
左から右 |
値が等価でない(eqvの否定) |
|
1600 |
&& |
左から右 |
論理AND |
1500 |
|| |
左から右 |
論理OR |
1300 |
: |
- |
変数束縛 |
:= |
- |
変数に値をセット |
演算子の定義
opスペシャルフォームにより演算子を新しく定義することができます。
演算子は関数またはスペシャルフォームと関連付いている必要があります。
op(2350, yfx, `+++)
最初の引数に優先順位、2番目の引数に結合性、最後の引数に演算子名を指定します。
2番目の引数の意味は以下の通りです。
値 | 意味 |
---|---|
fx |
後置演算子 結合しない |
fy |
後置演算子 結合する |
xf |
前置演算子 結合しない |
yf |
前置演算子 結合する |
fxf |
中置演算子 結合しない |
yxf |
中置演算子 左から右 |
fxy |
中置演算子 右から左 |
ライブラリリファレンス
四則演算
加算・乗算
add(1, 2, 3)
`+(1, 2, 3)
mul(1, 2, 3)
`*(1, 2, 3)
引数で与えられた値を全て足す、または掛けます。
減算・除算
sub(1, 2, 3)
`-(1, 2, 3)
div(1, 2, 3)
`/(1, 2, 3)
第1引数から第2引数以降を引く、または割ります。
第1引数のみが与えられた時は符号反転、または逆数を得ます。
整数の除算
quotient(13, 4)
remainder(13, 4)
modulo(13, 4)
quotientは整数で除算した商を返します。
remainderとmoduloは整数で除算した余りを返します。
remainderとmoduloの違いは以下の通りです。
第1引数 | 第2引数 | remainder | modulo |
---|---|---|---|
13 |
4 |
1 |
1 |
-13 |
4 |
-1 |
3 |
13 |
-4 |
1 |
-3 |
-13 |
-4 |
-1 |
-1 |
比較演算
等値
eqv(1, 2)
eqvは値が等しいかを判定しますが、構造までは判定しません。
数値
`=(1, 1, 1, 1)
`!=(1, 5, 3, 4)
`<(1, 2, 3, 4)
`<=(1, 2, 2, 3)
`>(4, 3, 2, 1)
`>=(3, 2, 2, 1)
引数が全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。
文字列
`string=("a", "a", "a")
`string!=("a", "c", b")
`string<("a", "aa", "b")
`string<=("a", "a", "b")
`string>("b", "aa", "a")
`string>=("b", "a", "a")
文字列が辞書順にが全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。
文字列(大文字小文字を区別しない)
`stringci=("a", "A", "a")
`stringci!=("a", "C", b")
`stringci<("a", "Aa", "b")
`stringci<=("a", "A", "b")
`stringci>("b", "Aa", "a")
`stringci>=("b", "A", "a")
文字列が辞書順にが全て等しい、等しくない、単調増加、広義の単調増加、単調減少、広義の単調減少であるかを判定します。
大文字小文字を区別しません。
論理演算
not(false)
引数がfalseでないときはfalse、falseのときはtrueを返します。
数学関数
三角関数
sin(0)
cos(0)
tan(0)
三角関数を返します。
逆三角関数
asin(0)
acos(0)
atan(1)
逆三角関数を返します。値域の外のときはNaNを返します。
指数・対数関数
exp(0)
log(1)
指数・対数関数を返します。値域の外のときはNaNを返します。
べき乗
expt(2, 3)
第1引数の第2引数乗を返します。
天井・床関数等
floor(2.3)
ceiling(2.3)
truncate(2.3)
round(2.3)
天井関数、床関数、小数点の切り捨て、小数点の四捨五入をします。
最大値・最小値
max(1, 3, 4, 2)
min(1, 3, 4, 2)
引数の中で最も大きい、または小さい値を取得します。
型述語
numberp(2.5)
integerp(2)
booleanp(false)
arrayp(#[1])
functionp(lambda(1))
第1引数が数、整数、論理値、配列、関数であるときにtrueを返します。
配列もオブジェクトになります。
functionpは継続のときもtrueを得ます。
配列操作
配列作成
list(1, 2, 3)
引数を要素とする配列を生成します。
配列の先頭・2番目以降の取得
first(#[1, 2, 3])
rest(#[1, 2, 3])
配列の先頭の要素、2番目以降の配列を取得します。
プロパティ値の設定
setprop(1, #[0, 1], 2)
プロパティの値を設定します。
連結
concat(#[1, 2, 3], #[4, 5, 6], #[7, 8, 9])
引数で与えらえた配列を連結します。
配列のmap
arraymap(add, #[1, 2, 3], #[4, 5, 6], #[7, 8, 9])
第2引数以降で与えられた配列を第1引数で与えられた関数に適用して、その値を要素とする配列を作成します。
上記の例では、add(1, 4, 7), add(2, 5, 8)が適用されて、#[12, 15]が結果と仕返されます。
文字列操作
文字列の連結
stringAppend("abc", "def", "ghi")
引数で与えられた文字列を連結します。
部分文字列の取得
substring("abcde", 1, 3)
第1引数で与えられた文字列の第2引数から(第3引数-1)番目までの文字列を取得します。
継続
callcc(lambda(k, k(765)))
現在の継続を取り出して、第1引数で与えられた関数の引数に渡します。
継続を使用することで、大域脱出や状態の復元を実現できます。
状態の復元の例を以下に示します。結果は1111となります。
> s:false
> add(346, callcc(lambda(k) { s:k; 961 }))
> s(765)
1111
その他
関数への適用
apply(add, #[1, 2, 3])
第1引数で与えられた関数へ第2引数で与えられた配列の要素を引数として適用します。
delayされたコード(promise)の実行
force(promise)
delayされたコード(promise)を実行します。結果はメモ化されます。
数値から文字列への変換
numberToString(100, 16)
第1引数で与えらえた数値を第2引数で与えられた基数で文字列に変換します。
基数は2〜36である必要があります。
基数が省略されたときは10とします。
文字列から整数への変換
stringToInteger("100", 8)
第1引数で与えらえた文字列を第2引数で与えられた基数で整数に変換します。
基数は2〜36である必要があります。
基数が省略されたときは10とします。
文字列から数値への変換
stringToNumber("100.3")
第1引数で与えらえた文字列を数値に変換します。
長さの取得
length(#[1, 2, 3])
length("abc")
配列または文字列の長さを取得します。
多値の生成
values(1, 2, lambda(1))
多値を生成します。
配列にはdekorationで表現可能な値しか要素になれませんでしたが、多値はdekorationで表現不可能な値も要素になれます。
文字の出力
p("console output")
コンソールに文字を出力します。
文字列表現の取得
toString(#[1, 2, 3])
文字列表現を取得します。
dekorationで表現可能な値のときはdekorationでシリアライズされた値が取得できます。
エラーの出力
error("Error occurred")
エラーを出力し、処理を終了させます。