関数
関数 binding、curried 呼び出し形式、ラムダ、型注釈と effect 注釈をまとめる。 record pattern 引数、method、role constraint も扱う。
宣言
my add x y = x + y
our greet name = "hello, " + name
pub double x = x + xbinding の左辺は pattern である。 pattern が直接の名前のとき、その後ろに続く pattern が関数引数になる。
| キーワード | 公開範囲 |
|---|---|
my | 現在の module / block で private |
our | 囲んでいる companion から見える(method の標準形) |
pub | module 境界をまたいで export される |
引数と curry
my add x y = x + y
my add_curried = \x -> \y -> x + y複数引数の binding は左から右に curry される。 add 1 は y を待つ関数である。 裸 application(add 1 2)でも C 風(add(1, 2))でも呼べる。 両方とも内部は curried application に lower される。
my inc = add 1
inc 41 // 42呼び出しの形
add 1 2 // 裸 application
add(1, 2) // C 風呼び出し
add: 1 // colon application(2 引数関数では稀)裸 application が標準形である。 視覚的に引数をまとめたいときや、裸だと token が流れて読みづらいときに C 風を使う。
ラムダ
my inc = \x -> x + 1
my add = \x y -> x + y
(inc 1, add 1 2)ラムダは \ で始まる。 複数引数のラムダ \x y -> ... は binding の頭と同じく curry される。
ラムダの引数自体も pattern である。
my add_pair = \(x, y) -> x + y
my greet = \{ name } -> "hello, " + name
(add_pair (1, 2), greet { name: "world" })特別な lambda 形式
\sub
\sub pattern -> body は、body に早期 return scope を持つ関数を作る。 return value はその関数の現在の呼び出しから抜ける。
my step = \sub n ->
if n > 0: return (n + 1)
0
say (step 41)\case
\case: は、引数を列挙した arm と照合する 1 引数関数を作る。
my classify = \case:
0 -> "zero"
_ -> "other"
say (classify 5)\catch
\catch: は 1 引数の handler 関数を作る。 引数は、列挙した operation arm と value arm で処理する effectful computation である。
act signal:
our ask: () -> int
my answer = \catch:
signal::ask(), k -> k 42
value -> value
say (answer signal::ask())型注釈
my double(x: int): int = x + x
my mark(label: str, value: 'a): 'a = value引数と戻り型の注釈は任意である。 省略すると推論が埋める。 API 境界のドキュメント、そのままだと開いてしまう generic の固定、曖昧さの解消、のいずれかに使う。
effect 付きの注釈
act console:
our read: () -> str
my ask(): [console] str = console::read()
my run_console(action: [console] 'a): 'a = catch action:
console::read(), k -> run_console (k "42")
run_console (ask())戻り型の角括弧形は effect row である。 [console] str は「str を返し、console effect を要求する」という意味。 [console; 'e] 'a のように tail 変数を入れると他の effect も開いたままになる。
オプショナル引数としての record pattern
my area {width = 1, height = 2} = width * height
area { width: 3 }
area { width: 3, height: 4 }
area {}default 付きの record pattern は、呼び出し側で各 field を省略可能にする。 default は左から右へ評価され、後ろの default は前の field を参照できる。
my f {a = 1, b = a + 1} = (a, b)with: 経由の method
struct point { x: int, y: int } with:
our p.norm2 = p.x * p.x + p.y * p.y
our p.scale n = point { x: p.x * n, y: p.y * n }with: 内の our recv.name args = body で、value.name args で呼べる method を定義する。 receiver recv は単なる名前(読みやすい名前を選ぶ)。
role の method
role Add 'a:
our a.add: 'a -> 'a
impl Add int:
our x.add y = std::int::add x yrole の method ヘッダーは with: と同じ receiver 形を使う。 : の後ろは receiver を当てた後の関数型(Add なら a.add は相手側の引数を取る関数)。
where 句
my twice(x: 'a) =
where 'a: Add
x.add xwhere 'a: Role は binding の type variable に role constraint を追加する。 binding の推論型にその constraint が伝播するので、twice 1 の呼び出しは Add int を要求する。
where 句は role の body や impl の body にも書ける。 role の body に書くと各 method に継承される。 impl の body に書くと、その impl 候補の前提条件になる。
ファイルの検査
yulang check examples/showcase.yu
# repo から動かす場合
cargo run -q -p yulang -- check examples/showcase.yucheck はファイルを検査する。 成功時は何も出力せず、失敗時だけ diagnostic を出す。 compiler IR には、推論された binding 型が含まれる。 residual な type variable(α、β、…)と role constraint(Add<α> => ...)も含まれる。 yulang dump examples/showcase.yu --poly で確認できる。
関連ページ
- pattern:
my/ ラムダ / 引数で共通の pattern 文法 - struct と role:companion method と role impl
- effect:引数 / 戻り型での effect row
- クックブック:これらを組み合わせるレシピ