About Fulyn

Fulynとは?

Fulynとは、ズバリOSECPU用の高級言語、そして関数型言語だ。

最新版の開発版バイナリは

https://github.com/lambdalice/OSETools/tree/develop/binary

からどうぞ。

Fulynを使うことにより、効率的なコーディングが可能になる(かもしれない)。

とりあえず、ここを見よ! => Fulyn_Samples

はじめに

Fulynは関数と文と式でできている。

関数

関数は、プログラムの一番表層に置かれる。

関数は、実行することができる。

文には、宣言と代入がある。

宣言文では、変数を宣言することができる。

代入文では、変数に式の評価結果を代入することができる。

式には、リテラル、変数、パターンマッチの三種類がある。

リテラルには、数字とラムダがある。

変数には、式の評価結果を保存できる。

パターンマッチで、分岐ができる。

まずは書いてみよう

プログラムの基本はHello, World!だ。

しかし、Fulynの配列はちょっとめんどくさいので、とりあえず一文字だけ表示してみよう。

   main
     putchar('a')
   end

まさに単純明快!Fulynはmain関数から実行される、これが重要。

Fulynは関数型言語なので、すべてが関数だ。そして、Fulynはすべてが宣言式と代入式で構成される。

putchar('a') は何にも代入していないように見えるが、それは後に説明する。

Fulynは高級言語だから、変数の定義もできる。

   main
     x :: int
     x = 0
     putnum(x)
   end

これで、int型の変数xが宣言され、xに0が代入され、0と表示された。もちろん、ASKAみたいにレジスタの定義なんてめんどくさいことは必要ない。

しかも、ものぐさなあなたのために、関数内で変数を宣言するときは、型推論機能により宣言を省略することができる。つまり、このように書ける。

   main
     x = 0
     putnum(x)
   end

型が「0」というリテラルから自動で推論され、「x :: int」を書いたことにしてくれるのだ。

ただし、Fulynではグローバルな変数の初期化ができない。そのため、グローバルでは、

   x :: int

としか書くことができない。代入は関数内からでしか不可能なのだ。

そして、Fulynは条件分岐が可能だ。条件分岐も式で、結果を返す。

条件分岐はこう書く。

   main
     x = 0
     y = 1
     ? gra(x, y)
     | true -> putnum(0)
     | false -> putnum(1)
   end

gra関数は、greater thanの略で、>と同じだ。

そう、1が表示される。

また、switch-caseのように使うことも可能だ。

   main
     x = rand()
     y = ? mod(x, 3)
         | 0 => 0xa
         | 1 => 0xb
         | () => 0
   end

これは、マッチする値 -> 返す式と書く。

Otherwiseは()と表す。

最後に、Fulynは四則演算も可能だ。

   main
     putnum(sub(mul(add(1, 2), 3), 4))
   end

もちろん、5が表示される。今のところ演算子は実装されていない。ごめん。

関数を書いてみよう

関数型言語なんだから関数が書けないとまずい。さっそく書いてみよう。

でもその前に、「関数型」の表し方について解説しておこう。

Fulynは関数型言語なので、関数自体をファーストクラスに扱うことができる。

要するに、関数と関数をとって関数を返す関数もつくれるわけだ(頭がこんがらがるかもしれないけど、ついてきてほしい)。

「関数型」は、このように記述する。

   func :: [int => int => int]

これは、Cで書くと

   int func(int,int);

と同じ意味になり、型宣言だが、働き上はプロトタイプ宣言と同じだ。

Haskellをやっている人ならわかりやすいだろう。引数の型を=>でつなぎ、最後に返す型を=>でつないで書く。

関数型だけは、グローバルであっても初期化が許されている。その代り、immutable(不変)となり、再代入ができない。(定義した関数を書き換えられたらとんでもないからね)

初期化をしなかった場合は、再代入が自由なただの変数となる。

では、関数を書いてみよう。Fulynでは関数の記述に二通りのシンタックスを用意している。

ひとつはλ(ラムダ)記法。これは簡単だ。

   func :: [int => int => int]
   func = x => y => add(x, y)

もちろん、受け取った二つの値を足して返す関数だ。数式に近いのでわかりやすいだろう。

ラムダは、引数を=>でつないで、=>の後に返す式を書く。ちょうど型の記述と同じ形になっている。

もうひとつは、Statement(ステートメント)記法。これが難しい。

Statement記法で関数を書くときにも、まずプロトタイプ宣言をする。

   func :: [int => int => int]

そして、次のように書く。

   func(x,y)
     _ = add(x, y)
   end

_ というのは特殊な変数で、"returner"と呼ばれる。returnerは、関数から抜けるときに格納している値を呼び出し元に戻す。関数の開始時には、初期値(intなら0、funcなら() => 0)で初期化されている。

変数に入れる必要のない値を_に入れることで葬る事ができる。returnerに値を代入する操作を「捨てる」ともいう。そのため、_ は"trash"でもある。捨てる動作は、戻り値の型にかかわらず行えるが、型が違う場合は無視される。

また、returnerの記述は省略することができる。つまり、このように書ける。

   func(x,y)
     add(x, y)
   end

これが、前の「putchar('a')」の文の正体だ。

さらに、Fulynでは $ 、その名を"breaker"という特殊な変数を用意している。

breakerは、参照先はreturnerと同じだが、評価された瞬間に関数を終了する。C言語などで言うreturnの操作が、ちょうどbreakerに代入する操作にあたる。

   func(x,y)
     $ = add(x, y)
     42
   end

と書いた時、42は評価されない。

Statement記法では、複文関数が組めるだけでなく、returnerとbreakerで複雑な流れ制御を実現できる。

配列

Fulynでは配列を扱うことができる。

しかし、Fulynには配列型がない。

そう、配列ですら関数なのだ。美しいじゃないか。

配列を作るには、array_new関数を使う。

(組み込み関数の一覧はそのうち作るよ)

   arr = array_new(3)

これで、長さが3の配列を確保できた。

次に、値を代入してみよう。

   array_set(arr, 1, 42)

これで、インデックス1に42が入った。

最後に、値を取得してみよう。

   putnum(arr(1))

もちろん、42が表示される。

この魔法のような仕組みは川合さんのおかげです。本当にありがとうございます。くれぐれも足を向けて寝ないように。

でも現状ちょっと不安定なので、関数呼び出しの中で呼ぶとたまにセキュリティ例外吐くかも。そういう時は、一回ローカル変数に代入してから使ってね。

いろいろ

ラムダでローカル関数を書く

Fulynでは関数がファーストクラスなので、いわゆるクロージャを書くこともできる。

クロージャを書くときは、型の宣言をしないといけないので、

   main
     func = x :: int => y :: int => add(x, y)
     func(3, 4)
   end

というように、引数の後に::で型を指定してあげる必要がある。

クロージャはローカル変数を利用できるし、普通の関数と同じように呼び出したり、他の関数に渡したり、いろいろできる。

末尾再帰最適化

Fulynでは、明示的宣言により末尾再帰の最適化ができる。

関数の頭に、

   main : tailcall
     main()
   end

というようにtailcallキーワードをつけてあげれば、スタックを食いつぶさない無限再帰となる。

ただし、呼び出し元には二度と戻ってこない。注意。

あとはサンプル読んでればわかるよ

こめんと


コメントお名前NameLink

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS