Login
Immutable PageDiscussionInfoAttachments

Revision 10 as of 2015-11-19 14:58:08

Clear message
moba/Koka

MMA

Kokaチュートリアルの適当日本語訳


これはC/C++やC#、JavaScriptなどのプログラマのためのKokaプログラミング言語の短い紹介文です

"Koka"は副作用を伴う計算から純粋な値を分離したfunction-oriented(関数指向?)な言語です('効果'の意味は英語でいうところの"effect"や"effective"です)。Kokaは柔軟で、ラージスケールなプログラムにおいてもデータの型の変更を容易にし、コードを正しくまとめる手助けを行うような機能を豊富に持っていて、JavaScriptに似た文法で小さな強い型付けを言語機能として持っています

より多くの背景情報が必要な場合はKoka research pageやLang.(2012/8)で発表した際のスライドを見てください

Hello world

慣習に倣って、まずはHello worldプログラムから始めましょう

   1 function main()
   2 {
   3   println("Hello World!") //println output
   4 }

関数はfunctionfunキーワードを使うことで定義されます。慣習的にはfunctionキーワードはトップレベルに対して、funキーワードは無名関数に対して使用されます

シーザー暗号化

ここでは文字列をシーザー暗号によって文字列中の小文字をアルファベットで3つだけ大きい方向に置き換える短いプログラム例を示します

   1 function encode( s : string, shift : int )
   2 {
   3   fun encodeChar(c) {
   4     if (c < 'a' || c 'z') return c
   5     base = (c - 'a').int
   6     rot  = (base + shift) % 26
   7     return (rot.char + 'a')
   8   }
   9 
  10   s.map(encodeChar)
  11 }
  12 
  13 function caesar( s : string ) : string
  14 {
  15   s.encode( 3 )
  16 }

この例では、encodeCharという1文字を受け取り、エンコードするローカル関数を定義しています。最後の文ではs.map(encodeChar)のように、encodeChar関数をstring型なs内の文字に対してそれぞれ適用し、シーザー暗号化された新しい文字列を返しています。関数内の最終文の結果も関数の値として返されますが、ふつうは明示的にreturnキーワードで関数を抜けます

このサンプルでは各文の終わりのセミコロンや定義がないことが見て取れます。"Koka"に文にセミコロンを自動で追加させたり、ブレース("{"と"}"のことです)の間で整理された形で定義を自動で入れさせたりすることでシンプルなレイアウト規則を使うことができます。長い文はさらにインデントを使うことで次の行に記述することができます。もちろん、必要であれば1行に複数の文を記述するなどのようにいつでもセミコロンを入れることができます

"Koka"は(例のオブジェクトとは対照的に)関数やデータを言語のコアから生成するようなfunction-orientedな言語です。特に、s.encode(3)という式はstring型のオブジェクトからencodeメソッドが選ばれているわけではなく、sが最初の引数となるencode(s,3)のような関数呼び出しの単純な糖衣構文です。同じように、c.intint(c)を呼ぶことで文字を整数に変換しています(どちらの式も同値です)。ドット記法は

   1 fun showit( s : string ) = s.encode(3).length.println

のように複数の呼び出しをまとめてチェインすることができ、直感的で非常に便利なものです。この例ではこの式はprintln(length(encode(s, 3)))に解釈されます。関数呼び出しの糖衣構文としてのドット記法の利便性はちょうど第1引数として型をとるような新しい関数を書くように、任意のデータ型をとる"組み込み"関数の拡張を簡単にするところです。ほとんどのオブジェクト指向言語では、例としてクラスがライブラリから提供されているような場合、クラス定義自身へメソッドを追加する必要がますが、これはいつでも可能というわけではありません

"Koka"は強い型付け言語です。ほとんどの型を推論できるような強力な型推論機構を使用し、ふつう、型が邪魔となることはありません。特に、いつでもローカル変数の型を省くことができます。これは例のbaserotからもわかります。関数の引数や関数の返り値の型を書いておくことで型推論を補助することができ、また、コンパイラからよりよいフィードバッグとともに便利なドキュメントを提供できます

encode関数において、s引数の型情報はlist型とstring型のどちらにも定義されたmap関数からいえるように型の注釈がない多相的なプログラムにおいて肝心です

無名関数

"Koka"は無名関数式が許されています。例として、encodeChar関数を定義する代わりに、map関数に関数式を直接渡してみましょう

   1 function encode2( s : string, shift : int )
   2 {
   3   s.map( fun(c) {
   4     if (c < 'a' || c 'z') return c
   5     base = (c - 'a').int
   6     rot  = (base + shift) % 26
   7     return (rot.char + 'a')
   8   });
   9 }

少しばかり、最後のブレースのあとの括弧がうっとおしいですね。"Koka"は便利なことに、関数呼び出しの代わりに無名関数を渡すことが許されています。たとえば、次の例では1から10までの数字を表示します

   1 function print10()
   2 {
   3   for(1,10) fun(i) {
   4     print(i)
   5   }
   6 }

これは for( 1, 10, fun(i){ print(i) } )に解釈されます。実際には、iを直接引数としてprintlnに渡していますが、(1,10,println)と書くことで、直接関数に渡すこともできます。

引数なしの無名関数はfunキーワードを除去し、"()"を除去するようなより短い記述が可能です。たとえば

   1 function print10()
   2 {
   3   repeat(10) {
   4     println("hi")
   5   }
   6 }

この分はrepeat( 10, fun(){println("hi")} )に解釈されます。この記法は特に、"Koka"の組み込み演算子ではないけど、ふつうの関数であるようなwhileループを記述するような場合に有用です。例えばこんな風に

   1 function print11() {
   2   var i := 10
   3   while { i >= 0 } {
   4     println(i)
   5     i := i-1
   6   }
   7 }

"Koka"はいつでも明示的に、コードが(ブレースの間の)関数が呼ばれる前に評価されたり(潜在的に複数回の)呼び出された関数(ブレースの間)によって評価される、ということを行います。

効果

"Koka"には自動で関数の内部で副作用が発生するかを推論することができる奇抜な機能があります。任意の作用がないことはtotal(あるいは<>)で表され、純粋数学関数と同じものとなります。もし関数が例外を上げるならば、作用はexnとなり、関数が終了しないかもしれない場合は、作用はdiv(分岐を意味している)となります。exndivの組み合わせはpureとなり、ちょうど、Haskellの純粋性の概念と直接対応しています。非決定的な関数はndet作用によって得ることができます。最悪の作用はioであり、それはプログラムが例外を上げたり、終了しなかったり、非決定的であったり、ヒープに読み書きを行ったり、任意の入出力操作を行うことを示しています。次のサンプルでは作用が現れる関数についての例を示しています。

function square1 x : int ) : total int
{
  return x*x
}

function square2( x : int ) : io int
{
  println( "a not so secret side-effect" )
  return x*x
}

function square3( x : int ) : div int
{
  square3( x )
  return x*x
}

function square4( x : int ) : exn int
{
  error( "oops" )
  return x*x
}

total作用であるとき、普通は次のように型注釈を省きます。

function square5( x : int ) : int = x*x

時には作用のある関数を書きますが、明示的に作用を書き下すのは面白くありません。そのような場合においては推論型を表すワイルドカード型を使うことができます。ワイルドカード型はアンダースコアが付加されている識別子を記述するかアンダースコア自身を記述することで表されます。例えば次のように

function square6( x : int ) : _e int
{
  println("I did not want to write down the io effect")
  return x*x
}

_eで推論された作用を表しています。

意味論

オプション引数と名前付き引数

さらなるデータ型

マッチ

todo