Open4

自作言語 Phosphor 進捗

ゆきくらげゆきくらげ

現状の見た目

/////////////
/* Library */
/////////////

data Nat: * [
  Zero: Nat;
  Suc: Nat => Nat;
];

showInt: Int => String
  = (x: Int) => `$x.toString()`: String;

showChar: Char => String
  = (x: Char) => `$x.toString()`: String;

showBool: Bool => String
  = [
    (true) => "true";
    (false) => "false";
  ];

showFloat: Float => String
  = (x: Float) => `$x.toString()`: String;

log: String => {Unit}
  = (x: String) => `() => console.log($x)`: {Unit};

natToInt: Nat => Int
  = [
    (Zero) => 0;
    (Suc(x: Nat)) => `$natToInt(x) + 1`: Int;
  ];

//////////
/* Main */
//////////

main: {Unit} = do{
  log(showInt(10));
};
ゆきくらげゆきくらげ

0.0.1までにやるべきこと

  • 多相型(ジェネリクス)

1.0.0までにやるべきこと

  • 型クラス
  • 型推論
  • 不完全なパターンマッチの検出
  • import機能の追加
ゆきくらげゆきくらげ

Document

基本的に上から順番に処理される.
副作用は起こさない.
JavaScriptにトランスパイルされる.
型推論がないのでいたるところに型を書かなければいけなく,やや不便.

コメント

// 一行コメント
/* 
複数行コメント
*/

プリミティブ型

// 型: カインド
String: * // "abc", "str" ...
Int: * // 1, 2, -4 ...
Float: * // 3.14, -1.23 ...
Bool: * // true, false
Unit: * // Unit
Char: * // 'a', 'b'
Array: * -> * // [1, 2, 3]
=>: * -> * -> * // 演算子
{}: * -> * // Effect型構築子

定数定義

// 定数名: 型 = 式; 
x: Int = 10;

// パターンマッチスタイルもある(後述)

関数型,ラムダ式,関数適用

// Int => Int はIntをとってIntを返す関数の型
// ラムダ式 (変数: 型) => 式
f: Int => Int = (x: Int) => add(x, 1);

// 複数の値をとる関数は,関数を返す高階関数として表現
g: Int => Int => Int = (x: Int) => (y: Int) => add(x, y);
// 次のようにも書ける
g: Int => Int => Int = (x: Int, y: Int) => add(x, y);

//関数適用
x: Int = f(2);

// 複数の値をとる関数への適用
y: Int = g(3)(4);
// 次のような糖衣構文がある
y: Int = g(3, 4);

配列型

// [要素0, 要素1,要素2]: Array(要素の型)
// 要素が0個のArrayの型を特定するために型情報が必要
x: Array(Int) = [1, 2, 3]: Array(Int);

// 現状アクセサがないので全く意味のない値になってしまっている.

データ型,パターンマッチ

// ex) IntとStringの直積
data TupleIntString: * [
  Cons: Int => String => TupleIntString;
];
// データ生成
x: TupleIntString = Cons(1, "one");

// データ取り出し
Cons(i: Int, str: String) = x; // i == 1, str == "one"

// ex) IntとStringの直和
data SumIntString: * [
  ConsInt: Int => SumIntString;
  ConsString: String => SumIntString;
];

//データ生成
intX: SumIntString = ConsInt(2);
stringX: SumIntString = ConsString("two"):

// パターンマッチ
// 複数のラムダ式を組み合わせたような構文
f: SumIntString  => String = [
  ConsInt(x: Int) => showInt(x);
  ConsString(x: String) => x;
];

// ex) 非負整数型
// データ型の定義
data Nat: * [ 
  Zero: Nat;
  Suc: Nat => Nat;
];

// 非負整数どうしの足し算
addNat: Nat => Nat => Nat
  = [
    (Zero, x: Nat) => x;
    (Suc(x: Nat), y: Nat) => add(x, Suc(y));
  ];

let式でスコープを切る

// 即時関数のように使える
fuga: Int = let{
  hoge: Int = 3;
  piyo: Int = add(hoge, 3);
  return piyo; //6を返す
};

foo: Int = fuga; // OK,foo==6
bar: Int = hoge; // エラー,hogeはスコープに存在しない

副作用型とdo式,main関数

// 副作用を起こす型は{...}で表される
// 型 {x} を x にしたときに副作用が実行されると思ってよい.

// 0 ~ 1のランダムな数値生成器
random: {Float}

// コンソールに出力する関数
log: String => {Unit} // UnitはTSにおけるvoidのようなもの

// 中身を取得できない以外は普通の値
hello: {Unit} = log("Hello World"); // 代入もできる

// do式の中で実行できる
// 実行するには =< という記号を使う
f: {Float} = do{
  x: Float =< random; // {Float}型がFloatになったので副作用が実行される
  y: Float =< random; // randomは実行されるたびに別の値を返すので一般に x != y

  ignore: Unit =< log("hoge"); // コンソールに"hoge" と表示する
  log("hoge"); // 左辺の値がUnitである場合は省略できる(通常使わないので)
  hello; // "Hello World"と出力する

  piyo: String = "piyo" // =による代入も普通に使える(これはlet式と同じ構文)
  log(piyo); // "piyo"と出力

  return add(x, y); //xとyの合計を返す
};
// f 全体の型は{Float}となっていることに注意
// f もまた別のdo式の中に埋め込むことができる

// main 関数は型{Unit}を持つ
main: {Unit} = do{
  x: Float =< f; // コンソールに"hoge"と二回出力して,"Hello World","piyo"と出力し,xに0 ~ 1のランダムな数二つの和を代入する.
}; // return Unit は省略できる.

FFIと実行時表現

// コードの一番頭に任意の行数埋め込める
// require文などを書く
// グローバル変数を書くのは非推奨
`
const X = require("...");
...
`

// 式にもJavaScriptのコードを埋め込める
// `JavaScript式`: 型
// 型は必須
x: Int
  = `3 + 5`: Int; // x == 8

// 導入された変数には頭に$がつく
y: Int
  = `3 + $x` // y == 11

// Int, Float, String, Char, Bool に対しては number, number, string, string, booleanが対応
// 1 == `1`
// 3.0 == `3.0`
// "str" == `"str"`
// 'a' == `"a"`
// true == `true`
// Array はただの配列
// [1, 2, 3] == `[1, 2, 3]`;
// Function もただの関数 
// (x: Int) => x == `(x) => x`
// 副作用型 {x} は0変数関数としてトランスパイルされる
// random: {Float} == `() => random()`

// したがってlog関数は次のように書ける
log: String => {Unit} 
  = (str: String) 
    => `() => console.log($str)`: {Unit};