=> |

September 11, 2026 · View on GitHub

引きようがないものを1ファイルにまとめたもの: 構文、他言語から 持ち込むと外れる習慣、そして「使おうと言われなくても手が伸びる」 範囲のライブラリのシグネチャ。凝縮元のリファレンス (handbook.ja.mdlanguage.ja.mdstdlib.ja.md) は合計15万トークン規模で、これは そのうちプロンプトに載る部分です。

ここに載せていないもの — 3D、2D、ソケット、パーサ、全文検索 — は §4で名前だけを挙げ、その章をまるごと出すコマンドを添えてあります。 署名の一覧だけでは動かし方が分からず、章を読めば分かるからです。

以下の```culebraブロックはすべてculebra test --doc docsが 実行するので、実装から乖離することはありません。行末の # => <値>は検証済みのstdout、# !! <パターン>は検証済みのthrow です。§4はjust gen-quick-guideがリファレンスから生成します — 手で編集しないでください。

目次

  1. プログラムを実行する
  2. 構文
  3. 持ち込むと外れる習慣
  4. シグネチャ索引
  5. テンプレート

1. プログラムを実行する

culebra prog.cul                 # バイトコードVM (既定)
culebra --jit prog.cul           # LLVM ORC JIT — 出力は同一
culebra build prog.cul -o prog   # AOT、自己完結バイナリ
culebra test                     # cwd以下のtest_*.culを全実行
culebra fmt -i .                 # その場で整形 (スタイル指定は無し)
culebra lint .                   # 静的検査; 警告1 / エラー2でexit
culebra docs -g 'Math.wrap'      # リファレンスから署名を引く
culebra docs stdlib Scene        # 名前空間の章をまるごと出す

ソースファイルの拡張子は.cul。プロジェクトファイルもマニフェストも パッケージマネージャもありません。標準ライブラリは全部importなしで スコープに入っています。

未定義の名前はプログラム実行に弾かれるので、ライブラリ名の当て 推量は途中まで動くことなく即座に失敗します:

# !! undefined variable 'puts'
puts('hi')

弾かれるのは名前であってメンバではありません。Math.abss(1)xs.len()lintを素通りし、その行が実行されて初めて落ちます。 存在しないプロパティはエラーでなくnilです。書いたら実行すること — lintを通しただけのプログラムは検査されていません。

リファレンス一式はバイナリの中にあり、実行中のビルドと常に一致します。 culebra docs -g <パターン>は一致したセクションを表示し、無ければ exit 1になるので、出力を読まずにAPIの実在を判定できます。パターン は識別子か語句であって、質問文ではありません。

2. 構文

2.1 束縛

束縛はmutを付けない限り不変です。裸のx = ...は新規束縛を作るか、 最も近い外側の束縛を再代入します。

x = 1       # 新規不変束縛、または外側を再代入
let y = 2   # 不変; 外側のシャドウは不可
mut z = 3   # 可変
z = 4       # bare再代入
z += 1      # -= *= /= %= **= @= も同様
inspect(z)  # => 5

引数も不変です。代入するとローカルコピーではなくエラーになります:

bump = fn (n) {
  n += 1
  n
}
inspect(try {
  bump(1)
} catch e {
  e.kind
})  # => 'ImmutableError'

キャプチャした外側の変数をシャドウする束縛の導入はコンパイル時 エラーです。1つの関数内でのブロックローカルな再束縛は問題ありません。

2.2 型

inspect(type_of(nil))     # => 'Nil'
inspect(type_of(true))    # => 'Bool'
inspect(type_of(42))      # => 'Long'
inspect(type_of(3.14))    # => 'Float'
inspect(type_of('hi'))    # => 'String'
inspect(type_of([1, 2]))  # => 'Array'
inspect(type_of({a: 1}))  # => 'Object'
inspect(type_of(fn () {
  1
}))                                    # => 'Function'
inspect(type_of((1, 'a')))             # => 'Tuple'
inspect(type_of({1, 2}))               # => 'Set'
inspect(type_of('hello'.slice(1, 3)))  # => 'StringView'

12番目がTensorです (§4)。クラス・モジュール・エラーはいずれも Objectの上に構築されています。

2.3 制御フロー

if / match / condは式、while / forは文です。

n = 7
sign = if n > 0 {
  1
} else if n < 0 {
  -1
} else {
  0
}
inspect(sign)  # => 1

label = match n {
  0 => 'zero',
  k if k < 10 => 'small',
  _ => 'large',
}
inspect(label)  # => 'small'

grade = cond {
  # 主語のないmatch
  n >= 90 => 'A',
  n >= 5 => 'B',
  _ => 'C',
}
inspect(grade)                    # => 'B'
inspect(n > 5 ? 'big' : 'small')  # => 'big'
for i in 0..3 {
  inspect(i)
}  # 半開; 0..=2は閉区間; `by k`で刻む
# => |
# 0
# 1
# 2

for k, v in {a: 1, b: 2} {
  inspect("{k}={v}")
}
# => |
# 'a=1'
# 'b=2'

break / continueは両方のループで使えます。どちらのループにも ラベルを付けられ、break / continueでそのラベルを指定すると、最内 ループではなくそのループを抜けます(次のiterationへ進みます)。ラベルは キーワードと同じ行に置き、変数名は取りません。nobreakブロックは ループがbreakされなかったときだけ走ります。while / if / matchは構文内に閉じたinit節を取れます:

for v in [1, 3, 5] {
  if v % 2 == 0 {
    break
  }
} nobreak {
  inspect('all odd')  # => 'all odd'
}

while mut i = 0; i < 3 {
  i += 1
}
if let m = 6; m > 5 {
  inspect('big')
}  # => 'big'

search: for row in [[1, 2], [3, 4]] {
  for cell in row {
    if cell == 3 {
      inspect(cell)  # => 3
      break search
    }
  }
}

2.4 関数

add = fn (a, b) {
  a + b
}
inspect(add(2, 3))  # => 5

typed = fn (a: Long, b: Long) -> Long {
  a + b
}
inspect(typed(2, 3))  # => 5

square = |x| x * x  # lambdaのbodyは既定で単一式
inspect(square(7))  # => 49

inspect([1, 2, 3].map(|x| x * 2))  # => [2, 4, 6]
inspect([[1, 2]].map(fn ((a, b)) {
  a + b
}))  # => [3]

値としてリテラルを束縛する (name = fn (...) { ... }) か、宣言形式 fn name(...) { ... }を使うかのどちらか。その場で渡すcallbackは |x|。波括弧も使え、|x| { ... }fn (x) { ... }と同じく文を 受け付ける。

名前を付けて呼ぶ関数、特に再帰関数には宣言形式を使うこと。 ジェネレータになれる、多重ディスパッチ に参加できる、fn.nameにソースレベルの名前が入るのはいずれも宣言形式 だけだが、それ以外の点では2つの形式は等価で、どちらのbodyでも裸の fnはその関数自身を呼ぶ:

fn fib(x) {
  if x < 2 {
    x
  } else {
    fn(x - 2) + fn(x - 1)
  }
}
inspect(fib(10))   # => 55
inspect(fib.name)  # => 'fib'

ブロックは最後の式に評価されるのでreturnはほとんど不要です。* 以降はキーワード専用、**restは未知のキーワードを、*restは余った 位置引数を集めます:

greet = fn (name, *, greeting = 'hi', **opts) {
  suffix = opts.has('loud') && opts.loud ? '!' : ''
  "{greeting}, {name}{suffix}"
}
inspect(greet('alice'))                     # => 'hi, alice'
inspect(greet('bob', greeting: 'yo'))       # => 'yo, bob'
inspect(greet('cy', loud: true))            # => 'hi, cy!'
inspect(greet('dee', **{greeting: 'hey'}))  # => 'hey, dee'

2.5 文字列

補間されるのは二重引用符だけ。単一引用符はリテラルです。

name = 'Culebra'
inspect("hello, {name}")  # => 'hello, Culebra'
inspect('hello, {name}')  # => 'hello, {name}'
inspect('a' + 'b')        # => 'ab'

size()はUTF-8のバイト数、foriter()はUnicodeスカラー 1個ずつ、graphemes()は書記素クラスタ1個ずつ進みます。s[i] 演算子はありません — バイトオフセットを取るsliceを使います。

inspect('café'.size())                        # => 5
inspect('café'.graphemes().collect().size())  # => 4
inspect('café'.slice(0, 1))                   # => 'c'
inspect('hello world'.split(' '))              # => ['hello', 'world']
inspect(['a', 'b'].join('-'))                  # => 'a-b'

"""はブロック文字列。"..."と同じく補間し、閉じ区切りのインデントを 取り除き、その直前の改行も落とす。閉じ"""は独立した行に置く。複数行の 文字列は\nを連結せずこれを使う。

sql = """
    SELECT *
    FROM t
    """

inspect(sql.lines())  # => ['SELECT *', 'FROM t']

2.6 イテレータ

rangeは遅延、iotaは確保します。.iter()はArrayを遅延化し、 チェーンは最初のconsumerで止まって中間Arrayを作りません。

inspect(iota(3))  # => [0, 1, 2]
inspect(range(1000).filter(|x| x % 2 == 0).map(|x| x * 3).take(4).collect())
# => [0, 6, 12, 18]
inspect(range(1, 11).reduce(0, |a, x| a + x))                  # => 55
inspect([1, 2, 3, 4].iter().zip(['a', 'b']).collect().size())  # => 2

for i, v in ['x', 'y'].enumerate() {
  inspect("{i}:{v}")
}
# => |
# '0:x'
# '1:y'

bodyにyieldを含むfnはジェネレータになり、呼ぶとイテレータが 返ります。

fn countdown(start) {
  mut i = start
  while i > 0 {
    yield i
    i -= 1
  }
}
inspect(countdown(3).collect())  # => [3, 2, 1]

iter() / has_next() / next()を持つオブジェクトなら何でもfor と全チェーンメソッドで使えます。

2.7 パターンマッチ

describe = fn (v) {
  match v {
    0 => 'zero',
    1 | 2 | 3 => 'small',
    n: Long if n > 100 => "big ({n})",
    n: Long => "int ({n})",
    s: String => "str ({s})",
    [] => 'empty',
    [x] => "one ({x})",
    [head, ...tail] => "head={head} rest={tail.size()}",
    {name} => "named {name}",
    _ => 'other',
  }
}
inspect(describe(2))            # => 'small'
inspect(describe(999))          # => 'big (999)'
inspect(describe([1, 2, 3]))    # => 'head=1 rest=2'
inspect(describe({name: 'z'}))  # => 'named z'

網羅性検査はありません。_の腕を用意してください。

2.8 エラー・deferdrop

throwできる値に制限はなく、tryは式です。

check = fn (x) {
  if x < 0 {
    throw "negative: {x}"
  }
  x
}
inspect(try {
  check(-1)
} catch e {
  e
})  # => 'negative: -1'
inspect(try {
  check(7)
} catch _ {
  0
})  # => 7

組み込みエラーはkindを持つObjectです:

inspect(try {
  1 / 0
} catch e {
  e.kind
})  # => 'ZeroDivisionError'

deferは囲むブロックのあらゆる脱出経路でLIFO順に走ります。引数 なしのdropプロパティを持つObjectは、最後の参照が消えた時点で それが呼ばれます。

{
  defer {
    inspect('second')
  }
  defer {
    inspect('first')
  }
  inspect('body')
}
# => |
# 'body'
# 'first'
# 'second'
{
  r = {drop: fn () {
    inspect('released')
  }}
  inspect('in scope')
}
inspect('after')
# => |
# 'in scope'
# 'released'
# 'after'

2.9 クラス・UFCS・多重ディスパッチ・trait

フィールドにはselfでアクセスします (thisではありません)。 クラス自体の呼び出しは.newの短縮形です。クラスもObjectリテラルと 同じ規則でdrop()を定義でき (2.8)、インスタンスへの最後の参照が 消えた時点でそれが呼ばれます。

class Car {
  wheels = 4  # デフォルト付きの宣言フィールド
  new(mpr) {
    self.miles = 0
    self.mpr = mpr
  }
  drop() {}  # 最後の参照が消えた時点でリソースを後始末
  run(n) {
    self.miles += self.mpr * n
  }
  get far() {
    self.miles > 10
  }  # 計算プロパティ、括弧なしで呼ぶ
  static unit() {
    Car(1)
  }
}

c = Car(5)
c.run(3)
inspect(c.miles)   # => 15
inspect(c.far)     # => true
inspect(c.wheels)  # => 4
inspect(type_of(c))  # => 'Car'

演算子はdunderメソッド (__add____eq____lt____index____setindex____call__等) に対応します。逆側メソッド (__radd__) はありません — その演算を所有する型の側にオーバーロード を置いてください。

自由関数f(x, ...)x.f(...)として呼べますが、既存のプロパティ やメソッドが常に優先されます:

double = fn (x) {
  x * 2
}
inspect(42.double())  # => 84

同名の自由関数を複数定義すると、宣言された引数型でディスパッチします:

class Circle {
  new(r) {
    self.r = r
  }
}
fn area(c: Circle) {
  3 * c.r * c.r
}
fn area(n: Long) {
  n
}
inspect(area(Circle(2)))  # => 12
inspect(area(10))         # => 10

traitは構造的です。メソッド名とアリティが一致するクラスはimpl 宣言なしで適合します。traitメソッドはデフォルト実装のbodyを持て、 @derive(Eq, Hash, Show, Comparable)が定型の適合メソッドを生成します。

trait Greeter {
  hello() -> String
}
class Bob {
  new(n) {
    self.n = n
  }
  hello() {
    "hi, {self.n}"
  }
}
greet = fn (g: Greeter) -> String {
  g.hello()
}
inspect(greet(Bob('Ann')))  # => 'hi, Ann'

enumはsum typeです。nullary variantはsingleton値、payload variantは コンストラクタで、matchで分解します。variantはそのままEqかつ Hashableなので、Object / Setのkeyになります。

enum Shape {
  Circle(Float),
  Rect(Float, Float),
  Origin,
}
area = fn (s) {
  match s {
    Circle(r) => 3 * r * r,
    Rect(w, h) => w * h,
    o: Origin => 0,
  }
}
inspect(area(Shape.Rect(2.0, 3.0)))          # => 6.0
mut seen = {}
seen[Shape.Origin] = 'origin'
inspect(seen[Shape.Origin])                  # => 'origin'
inspect({Shape.Origin, Shape.Origin}.size())  # => 1

2.10 エフェクト

performは、意味を外側のhandleが決める操作を発行します。継続は multi-shotです。

effect fn ask()

inspect(handle {
  perform ask() * 2
} with ask(resume) {
  resume(21)
})  # => 42

resumeを呼ばない節は残りの計算を捨てます — これはまさに例外です。 with return(v) { ... }は正常完了時の値を写します。

2.11 モジュール

export / importはトップレベル専用なので、依存グラフはparse時に 確定します。

# doctest: skip
# lib.cul
let greet = fn (n) {
  "hello, {n}"
}
export {greet}
# doctest: skip
# main.cul
import lib from './lib.cul'
inspect(lib.greet('world'))  # => 'hello, world'

パスはimport元ファイルからの相対で解決される単一引用符リテラルです。 各モジュールの評価は1回だけ。循環はImportErrorになります。

2.12 オプショナル型注釈

注釈が検査されるのは代入・引数渡し・戻り値の3境界だけで、それ以外 では検査されません。Long | StringはUnion、T?T | NilArray<Long>は要素型を文書化しますが要素ごとの検査はしません。

show = fn (x: Long | String) -> String {
  to_string(x)
}
inspect(show(1))     # => '1'
inspect(show('hi'))  # => 'hi'

3. 持ち込むと外れる習慣

各行は、他言語の習慣のまま書くと失敗するか、黙って別のものになる ケースです。

つい書くものCulebraでは
'text {x}'の補間補間されるのは"..."のみ。'...'はリテラル
puts / console.log / 改行付きのprint(x)inspect(x)はクォート付きデバッグ形式、println(x)は生+改行、print(x)は生
thisself
x |> f()UFCSのx.f()。パイプライン演算子は無い
1要素のSetとして{3}SyntaxError。1要素は{3,}{}は空Object
集合のa | b / a & bメソッド: a.union(b) / a.intersect(b) / a.diff(b)
{a: 1} + {b: 2}TypeError。Objectのマージ演算子は無い
Stringへのs[0]TypeError。バイトオフセットを取るs.slice(0, 1)を使う
-7 % 3 == 2 (Python)-1 — 符号は被除数に従う (C流)
-7 / 2 == -4 (Python)-3 — Long除算はゼロ方向に切り捨て
if [] { } / if '' { }TypeError。判定できるのはBool / Long / Floatのみ
0 == falsefalse — 型をまたぐ暗黙変換は無い
.length / .count.size()。存在しないプロパティはnilなので.lengthはraiseせずnilになる
.append(x).push(x)
xs.sort(|a, b| ...) — 比較関数を渡すxs.sorted_by(key)が取るのは比較関数ではなくキー関数。降順はreverse: true<=>は無い
xs.take(n) / xs[:n]xs.slice(0, n)takeはIteratorのメソッド — xs.iter().take(n).collect()
obj.items() / obj.entries() / obj.to_array()for k, v in obj、またはobj.keys().map(|k| (k, obj[k]))
del a[i] / a.splice(i, 1)a.remove_at(i)。取り除いた要素を返す
自分で決めていない文字列をキーにしたObjectdropはRAIIフック (2.9) なので、そのキーだけDropContractErrorになる — 接頭辞を付ける
obj['missing']KeyErrorobj.missingnilobj.get('missing', dflt)はfallbackを取る
'ab' * 3TypeError。文字列の繰り返し演算子は無い
s.find(x) / s.indexOf(x)s.index_of(x) — バイトオフセット、無ければ-1
s.ljust(10) / s.padStart(10) / s.zfill(5)補間の書式指定: "{s:<10}""{n:05}"
引数なしのs.split()(空白で分割)s.split_whitespace()splitは必ず区切りを取る
引数への代入引数は不変 — ImmutableError
同一スコープでx = 1を2回ImmutableErrormut x = 1と宣言する
and / or / not&& / || / !
elifelse if
コメントが#だけ、または//だけ両方使える。加えて/* ... */
async / await設計上存在しない — I/Oはblocking。Isolate / Parallelを使う
パッケージマネージャ標準ライブラリは全部importなしでスコープにある

エラーにならず値が返るぶん見落としやすいものが3つ:

# split が返すのは String ではなく StringView。安いが type_of は異なる
inspect(type_of('a,b'.split(',')[0]))  # => 'StringView'

# 存在しないプロパティは黙って nil
inspect([1, 2].length)  # => nil
inspect([1, 2].size())  # => 2

# どの arm にも当たらない match はエラーではなく nil。lint が指摘するのは
# 対象が enum のときだけなので、それ以外の match には `_` の arm を置く
inspect(match 9 {
  0 => 'zero',
})  # => nil

慣用形

上の表はエラーになるもの。ここに挙げるのは動くが、この言語の書き方では ないもので、何も警告してくれない。右の列で書く。

他言語からの移植では、if/elif/elsecase/switchの連鎖はここでは match/condに対応する。if/else ifの連鎖としてそのまま持ち込むのは、 移植でもっとも起こりやすい取り違えである。

動くがこう書く
値としてのif c { a } else { b }c ? a : b
値を返すif / else ifの連鎖1つの値を定数群と比較するならmatch、互いに無関係な条件ならcond
ClassName.new(...)ClassName(...)class定義型は呼び出し可能で、コンストラクタ呼び出しの糖衣になる
"a" + x + "b"(値の前後にリテラル文字列を継ぎ足す)"a{x}b"
i = i + 1i += 1
x.size() == 0 / > 0x.empty() / !x.empty()
ループの外にmut i = 0を置いてi += 1for i, v in xs.enumerate()
mut out = [] + for + out.push(f(x))xs.map(f)filterも同様)
mut t = {} + for + t[k] = vxs.map(|x| (k(x), v(x))).to_object()
mut found = false + while !foundfor x in xs { … break }xs.find(p)
mut hit = false + for + if p(x) { hit = true; break }xs.any(p)(逆はxs.all(p)
"a\n" + "b\n""""ブロック
.map(fn (x) { expr }).map(|x| expr)
range(0, n)range(n)
iota(n).map(|_| v)repeat(n, v)
for i in 0..xs.size() { xs[i] … }for x in xs
mut i = start; while i < end { …; i += 1 }for i in start..end { … }
{k1: v1, k2: obj.k2, k3: obj.k3}(1つ変えるために全フィールドを手コピー){...obj, k1: v1}
if cond { stmt }elseなしの単文stmt if cond(否定はstmt unless cond
if x == nil { x = v } / if !d.has(k) { d[k] = v }x ??= v / d[k] ??= vobj.keyにも使える)
"{a.b(c).d ?? e}"(長く入れ子になった式をそのまま埋め込む)let x = a.b(c).d ?? eとしてから"{x}"

condは主語のないmatchなので、互いに無関係な条件の連鎖は cond { a > 1 => …, b < 2 => …, _ => … }になる。完走したかどうかを 知りたいループはフラグでなくnobreakブロックを使う。

これはCulebraで書かれた型に限らず、ライブラリが載せているコンストラクタ すべてに当てはまる。Scene.Image(4, 4)Scene.Image.new(4, 4)と、 Channel()Channel.new()と同じ呼び出しになる。§4の索引が.new(...)と 表記しているのは、説明対象がそのメソッド自体だからである。

4. シグネチャ索引

レシーバ名は慣例です: sはString、aはArray、oはObject、 itはIterator、reはコンパイル済みRegex、fは開いたFile。 レシーバが付いていないエントリ (セットのメソッドcontains(x)Httpjson()等) は、そのグループ自身の型のメソッドです。 **(experimental)**が付いたグループはビルド時のopt-inで、リリースバイナリ には入っていないことがあります(stdlib.ja.mdの該当節に 書いてあります)。

末尾の表にある名前空間は、一覧にせず名前だけを挙げてあります。どれも状態を 持つ仕組み — viewとフレームループ、開いた接続、文法、エミッタ — を 動かすもので、署名は「何を呼ぶか」は言っても「どの順で、何に対して、 結果は誰のものか」を言いません。culebra docs stdlib <名前>でその章が まるごと(前書きも小節も込みで)出ます。その名前空間を使う前に読んで ください。署名から推測して書くと、lintは通るのに動かないコードになり ます。

文字列メソッド — s.size() -> Long; s.empty() -> Bool; s.presence() -> String | StringView | Nil; s.upper() -> String; s.lower() -> String; s.capitalize() -> String; s.title() -> String; s.normalize(form: StringLike = "NFC") -> String; s.eq_ignore_case(other: StringLike) -> Bool; s.reverse() -> String; s.repeat(n: Long) -> String; s.truncate(max: Long, ellipsis: StringLike = "...") -> String; s.trim() -> String; s.trim_start(chars: StringLike = "") -> String; s.trim_end(chars: StringLike = "") -> String; s.tr(from: StringLike, to: StringLike) -> String; s.split(sep: StringLike, limit: Long = 0) -> Array; s.rsplit(sep: StringLike, limit: Long = 0) -> Array; s.split_whitespace() -> Array; s.split_once(sep: StringLike) -> Tuple | Nil; s.rsplit_once(sep: StringLike) -> Tuple | Nil; s.split_iter(sep: StringLike) -> Iterator; s.lines() -> Array; s.replace(pat: String | Regex, repl: String | Function) -> String; s.contains(sub: StringLike) -> Bool; s.count(sub: StringLike) -> Long; s.starts_with(prefix: StringLike) -> Bool; s.ends_with(suffix: StringLike) -> Bool; s.index_of(sub: StringLike, start: Long = 0) -> Long; s.last_index_of(sub: StringLike) -> Long; s.strip_prefix(prefix: StringLike) -> String; s.strip_suffix(suffix: StringLike) -> String; s.replace_first(pat: String | Regex, repl: String | Function) -> String; s.is_digit() -> Bool; s.is_alpha() -> Bool; s.is_alnum() -> Bool; s.is_space() -> Bool; s.is_ascii() -> Bool; s.slice(start: Long, end: Long) -> StringView; s.view() -> StringView; s.to_string() -> String; s.iter() -> Iterator; s.code_points() -> Iterator; s.graphemes() -> Iterator; s.words() -> Iterator; s.sentences() -> Iterator; s.bytes() -> Iterator; String.from_code_point(cp: Long) -> String; String.from_code_points(cps: Array) -> String; String.from_bytes(bytes: Array) -> String

配列メソッド — a.size() -> Long; a.empty() -> Bool; a.presence() -> Array | Nil; a.push(x: Any) -> Nil (破壊的); a.pop() -> Any (破壊的); a.extend(other: Array) -> Nil (破壊的); a.insert(i: Long, x: Any) -> Nil (破壊的); a.remove_at(i: Long) -> Any (破壊的); a.get(i: Long, fallback: Any) -> Any; a.slice(start: Long, end: Long) -> Array; a.join(sep: String) -> String; a.contains(v: Any) -> Bool; a.index_of(v: Any) -> Long; a.reverse() -> Nil (破壊的); a.map(f: Function) -> Array; a.filter(f: Function) -> Array; a.for_each(f: Function) -> Nil; a.reduce(init: Any, f: Function) -> Any; a.find(f: Function) -> Any; a.any(f: Function) -> Bool; a.all(f: Function) -> Bool; a.flat_map(f: Function) -> Array; a.sum() -> Long | Float; a.product() -> Long | Float; a.min() -> Any; a.max() -> Any; a.min_by(f: Function) -> Any; a.max_by(f: Function) -> Any; a.to_set() -> Set; a.to_object() -> Object; a.group_by(f: Function) -> Object; a.partition(p: Function) -> Tuple; a.unzip() -> Tuple; a.sort(reverse: Bool = false) -> Nil (破壊的); a.sorted(reverse: Bool = false) -> Array; a.sort_by(key: Function, reverse: Bool = false) -> Nil (破壊的); a.sorted_by(key: Function, reverse: Bool = false) -> Array

オブジェクトメソッド — o.size() -> Long; o.empty() -> Bool; o.presence() -> Object | Nil; o.keys() -> Array; o.values() -> Iterator; o.has(key: String) -> Bool; o.get(key, fallback) -> Any; o.get_or_put(key, init) -> Any (破壊的); o.remove(key: String) -> Nil (破壊的)

セットのメソッド — size(); empty() -> Bool; presence(); contains(x) -> Bool; union(b); intersect(b); diff(b); sym_diff(b); subset(b) -> Bool; superset(b) -> Bool; to_array(); iter(); add(x); remove(x)

イテレータメソッド — iter(); it.map(f); it.filter(p); it.take(n); it.skip(n); it.take_while(p); it.skip_while(p); it.step_by(n); it.distinct(); it.tap(f); it.scan(init, f); it.flatten(); it.chunk_by(f); it.chunks(n); it.windows(n); it.flat_map(f); it.chain(other); it.zip(other); it.enumerate(); it.collect(); it.join(sep); it.for_each(f); it.reduce(init, f); it.find(p); it.any(p); it.all(p); it.count(); it.first(); it.last(); it.nth(n); it.position(p); it.contains(v); it.sum(); it.product(); it.min(); it.max(); it.min_by(f); it.max_by(f); it.to_set(); it.to_object(); it.group_by(f); it.partition(p); it.unzip()

コア組み込み関数 — to_long(v: Any, *, base: Long = 10) -> Long; to_float(v: Any) -> Float; to_string(v: Any) -> String; class_of(v: Any) -> Object?; type_of(v: Any) -> String; range(n: Long, *, step: Long = 1) -> Iterator; range(start: Long, end: Long, *, step: Long = 1) -> Iterator; iota(n: Long) -> Array; iota(start: Long, end: Long) -> Array; repeat(n: Long, value: Any) -> Array; grid(x_range: Range, y_range: Range) -> Iterator

Math — Math.pi; Math.e; Math.inf; Math.nan; Math.abs(x: Long|Float) -> Long|Float; Math.min(a, b, ...) -> Long|Float; Math.max(a, b, ...) -> Long|Float; Math.log(x: Long|Float) -> Float; Math.exp(x: Long|Float) -> Float; Math.sqrt(x: Long|Float) -> Float; Math.sin(x) -> Float; Math.cos(x) -> Float; Math.tan(x) -> Float; Math.asin(x) -> Float; Math.acos(x) -> Float; Math.atan(x) -> Float; Math.atan2(y, x) -> Float; Math.floor(x: Long|Float) -> Long; Math.ceil(...) -> Long; Math.round(...) -> Long; Math.f32(x: Long|Float) -> Float; Math.pow(base: Long, exp: Long) -> Long; Math.sign(x: Long) -> Long; Math.clamp(x: Long|Float, lo: Long|Float, hi: Long|Float) -> Long|Float; Math.wrap(x: Long, n: Long) -> Long

IO — IO.inspect(x: Any) -> Nil; IO.print(x: Any) -> Nil; IO.println(x: Any = '') -> Nil; IO.input() -> String; IO.stdin() -> reader; .read(); .read(n: Long); .lines(); IO.einspect(x: Any) -> Nil; IO.eprint(x: Any) -> Nil; IO.eprintln(x: Any) -> Nil; IO.stdin_is_terminal() -> Bool; IO.stdout_is_terminal() -> Bool; IO.stderr_is_terminal() -> Bool; IO.capture(f: Function) -> String

FS — FS.join(parts...: String) -> String; FS.sep() -> String; FS.basename(path: String) -> String; FS.dirname(path: String) -> String; FS.extension(path: String) -> String; FS.stem(path: String) -> String; FS.read(path: String) -> String; FS.write(path: String, content: String) -> Nil; FS.exists(path: String) -> Bool; FS.is_file(path: String) -> Bool; FS.is_dir(path: String) -> Bool; FS.size(path: String) -> Long; FS.list_dir(path: String) -> Array; FS.mkdir(path: String) -> Nil; FS.temp_dir() -> String; FS.mkdtemp(prefix: String) -> String; FS.remove(path: String, recursive: Bool = false) -> Nil; FS.rename(src: String, dst: String) -> Nil; FS.copy(src: String, dst: String, recursive: Bool = false) -> Nil; FS.chmod(path: String, mode: Long) -> Nil; FS.chown(path: String, owner = nil, group = nil) -> Nil; FS.stat(path: String) -> Object; FS.walk(path: String) -> Array; FS.glob(pattern: String) -> Array; FS.watch(path: String, recursive: Bool = true, match: Array? = nil) -> WatchHandle; FS.abspath(path: String) -> String; FS.realpath(path: String) -> String; FS.normpath(path: String) -> String; FS.is_abs(path: String) -> Bool; FS.symlink(target: String, link: String) -> Nil; FS.readlink(path: String) -> String; FS.is_symlink(path: String) -> Bool; p.join(other); p.resolve(); p.exists(); p.is_file(); p.is_dir(); p.read(); p.write(s); p.mkdir(); p.remove(recursive=false); p.rename(dst); p.list(); p.glob(pattern); p.walk(); p.str()

File — File.open(path: String, mode: String = "r") -> File; File.with(path: String, mode: String = "r", fn: Function) -> Any; f.read() -> String; f.read(n: Long) -> String; f.lines() -> Iterator; f.chunks(n: Long) -> Iterator; f.write(data: String) -> Nil; f.flush() -> Nil; f.seek(offset: Long, whence: String = "set") -> Nil; f.tell() -> Long; f.close() -> Nil

Time — Time.now() -> Instant; Time.monotonic() -> Float; Time.sleep(secs: Float) -> Nil; Time.from_iso(s: String) -> Instant; Time.from_unix(secs: Long|Float) -> Instant; Time.from_parts(p: Object, utc: false) -> Instant; Time.parse(s: String, fmt: String) -> Instant; t.iso(utc: true) -> String; t.format(fmt: String, utc: false) -> String; t.parts(utc: false) -> Object; t.weekday(utc: false) -> Long; t.add(years=0, months=0, days=0, hours=0, minutes=0, seconds=0, utc: false) -> Instant; t.start_of(unit: String, utc: false) -> Instant; t.unix() -> Float; t.unix_nanos() -> Long; d.seconds() / .milliseconds() / .minutes() / .hours() / .days() -> Float; d.abs() -> Duration

Random — Random.seed(n: Long) -> Nil; Random.int(lo: Long, hi: Long) -> Long; Random.uniform(lo: Float, hi: Float) -> Float; Random.gauss(mu: Float, sigma: Float) -> Float; Random.shuffle(a: Array) -> Nil; Random.weighted_choice(pop: Array, weights: Array) -> Any; Random.choice(pop: Array) -> Any

Sys — Sys.exit(code: Long) -> Nil; Sys.env(name: String, fallback = '') -> Any; Sys.set_env(name: String, value: String) -> Nil; Sys.getcwd() -> String; Sys.chdir(path: String) -> Nil; Sys.data_dir(app: String) -> String; Sys.time() -> Float

Tensor — Tensor.zeros(...) -> Tensor; Tensor.ones(...); Tensor.randn(...); Tensor.from(arr: Array) -> Tensor; Tensor.concat(parts: Array, axis: Long = 0) -> Tensor; Tensor.where(cond: Tensor, a, b) -> Tensor; Tensor.index_add(indices: Tensor, values: Tensor, target_shape: Array) -> Tensor; Tensor.scatter_to_axis(indices: Tensor, values: Tensor, size: Long) -> Tensor; Tensor.from_csv(path: String) -> Tensor; Tensor.eval(t1, t2, ...) -> Nil; .shape() -> Array; .dot(other: Tensor) -> Tensor; .linear_sigmoid(x, b) -> Tensor; .pow(exp) -> Tensor; .gt(other) / .lt(other) / .ge(other) / .le(other) / .eq(other) / .ne(other) -> Tensor; .tanh() / .sin() / .cos() -> Tensor; .clamp(lo, hi) -> Tensor; .rope(pos: Long, base) -> Tensor; .transpose() -> Tensor; .permute(axes: Array) -> Tensor; .slice(start, end) -> Tensor; .narrow(params: Array) -> Tensor; .reshape(dims: Array) -> Tensor; .unfold(params: Array) -> Tensor; .pad(params: Array) -> Tensor; .fold(params: Array) -> Tensor; .sum() -> Float; .sum(axis: Long?) -> Tensor; .mean() / .mean(axis); .max() / .max(axis); .argmax(axis: Long) -> Tensor; .index_select(indices: Tensor) -> Tensor; .softmax_cross_entropy(targets: Tensor) -> Tensor; .to_array() -> Array; .item() -> Float; .requires_grad() -> Tensor; .backward() -> Nil; .grad() -> Tensor; .zero_grad() -> Nil; .detach() -> Tensor; Tensor.use_cpu() -> Nil; Tensor.use_gpu() -> Nil; Tensor.use_auto() -> Nil; Tensor.gpu_available() -> Bool; Tensor.device() -> String

JSON — JSON.stringify(v, indent=0, sort_keys=false, lines=false) -> String; JSON.parse(s, lines=false, number_mode='auto', jsonc=false) -> Any

Args — Args.parse(argv: Array, spec: Object) -> Object; Args.try_parse(argv, spec) -> Object; Args.help(spec: Object) -> String

Proc — Proc.run(cmd: Array, cwd=nil, env=nil, stdin="", check=false, timeout=0, share=nil, inherit_env=true) -> Object; Proc.all(commands: Array<Array>, limit: Long = <CPU数>, timeout: Long = 0, fail_fast: Bool = false, retries: Long = 0, share: Object? = nil, inherit_env: Bool = true) -> Array; Proc.race(commands: Array<Array>, share: Object? = nil, inherit_env: Bool = true) -> Object; Proc.spawn(cmd: Array, cwd=nil, env=nil, stdin="", share=nil, inherit_env=true) -> handle; h.wait(); h.poll(); h.kill(sig = 15)

Isolate — Isolate.spawn(fn, *args) -> handle; h.join(); h.poll(); Channel.new(cap = 1) -> (tx, rx); tx.send(v); tx.clone(); rx.clone(); tx.drop(); rx.drop(); rx.recv(); rx.try_recv(); rx.drain(max = nil); Channel.fan_in(sources: [rx]) -> rx; Channel.fan_in(items, fn) -> rx; Parallel.map(items, fn, limit = <コア数>); Parallel.each(items, fn, limit = <コア数>); Parallel.map_settled(items, fn, limit = <コア数>); Parallel.race(items, fn, limit = <コア数>); Signal.notify(tx); Signal.reset(); SharedBuffer.new(count, Class) -> buffer; SharedBuffer.file(path, count, Class) -> buffer; SharedBuffer.shared(count, Class) -> buffer; buffer.with_lock(fn)

Matchers — assert_true(x: Bool, label: String? = nil) -> Nil; assert_false(x: Bool, label: String? = nil) -> Nil; assert_eq(a, b, label: String? = nil) -> Nil; assert_ne(a, b, label: String? = nil) -> Nil; assert_lt(a, b, label: String? = nil) -> Nil; assert_le(a, b, label: String? = nil) -> Nil; assert_gt(a, b, label: String? = nil) -> Nil; assert_ge(a, b, label: String? = nil) -> Nil; assert_throws(kind: String, f: Function) -> Nil; assert_close(a: Float, b: Float, tol: Float, label: String? = nil) -> Nil

Regex — Regex.compile(pat) -> Regex; Regex.compile(pat, flags) -> Regex; Regex.escape(s) -> String; Regex.interp(x) -> String; Regex.find(pat, s); Regex.match(pat, s); Regex.find_all(pat, s); Regex.test(pat, s); Regex.split(pat, s); Regex.replace_all(pat, s, repl) -> String; Regex.replace_first(pat, s, repl) -> String; re.test(s) -> Bool; re.find(s); re.match(s); re.find_all(s) -> [Match]; re.find_all_str(s) -> [String]; re.find_all_index(s) -> [Int]; re.count(s) -> Int; re.find_iter(s) -> Iterator; re.replace_all(s, repl) -> String; re.replace_first(s, repl) -> String; re.split(s) -> [String]

Http — json(); Http.get(url, headers=nil, timeout=0, follow_redirects=true); Http.delete(url, headers=nil, timeout=0, follow_redirects=true); Http.head(url, headers=nil, timeout=0, follow_redirects=true); Http.post(url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.put(url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.request(method, url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.sse(url, on_event, headers=nil, timeout=0, follow_redirects=true); Http.client(base_url, headers=nil, timeout=0, follow_redirects=true); Http.server(); Http.ws(url); Http.sse(url, on_event, headers=nil, timeout=0, follow_redirects=true) -> Object; Http.client(base_url, headers=nil, timeout=0, follow_redirects=true) -> Object; Http.server() -> Object; static(mount, dir); sink.write(chunk); bind(port, host="0.0.0.0") -> Long; serve(workers=0); serve_async(workers=0); listen(port, host="0.0.0.0", workers=0); listen_async(port, host="0.0.0.0", workers=0) -> Long; stop(); close(); ws.receive(); ws.try_receive(); ws.send(msg); ws.set_timeout(ms); ws.close(); ws.is_open(); Http.ws(url) -> Object; dir.read(path); dir.exists(path)

Encoding — Encoding.html; Encoding.html.escape(s) -> String; Encoding.html.unescape(s) -> String; Encoding.base64; Encoding.base64.encode(s) -> String; Encoding.base64.decode(s) -> String; Encoding.hex; Encoding.hex.encode(s) -> String; Encoding.hex.decode(s) -> String; Encoding.url; Encoding.url.encode(s) -> String; Encoding.url.decode(s) -> String

Compress — Compress.gzip(data: String) -> String; Compress.gunzip(data: String) -> String; Compress.deflate(data: String, level: Long = -1) -> String

Hash — Hash.sha256(data: String) -> String; Hash.sha1(data: String) -> String; Hash.sha512(data: String) -> String; Hash.md5(data: String) -> String; Hash.hmac_sha256(key: String, data: String) -> String; Hash.hmac_sha1(key: String, data: String) -> String; Hash.hmac_sha512(key: String, data: String) -> String

CSV — CSV.parse(text, delimiter=",", header=false, types=nil) -> Array; CSV.stringify(rows: Array, delimiter: String = ",") -> String

Env — Env.parse(text: String) -> Object; Env.load(path: String = ".env", override: Bool = false) -> Object

UUID — UUID.v4() -> String; UUID.v7() -> String

Term — Term.fg(s, n) -> String; Term.bg(s, n) -> String; Term.rgb(s, r, g, b) -> String; Term.red(s); Term.bold(s); Term.dim(s); Term.underline(s); Term.reverse(s); Term.style(fg:, bg:, bold:, dim:, underline:, reverse:) -> String; Term.clear() -> String; Term.move(x, y) -> String; Term.hide(); Term.show(); Term.cols(); Term.rows() -> Long; Term.size() -> (Long, Long); Term.width(s) -> Long; Term.flush(); screen.size(); cols(); rows(); screen.clear(); screen.set(x, y, glyph, style = ""); screen.put(x, y, s, style = ""); screen.render() -> String; screen.flush(); screen.poll(timeout) -> Object?

Log — Log.debug(msg: String, fields: Object = {}); Log.info(msg, fields = {}); Log.warn(msg, fields = {}); Log.error(msg, fields = {}); Log.with(fields: Object) -> logger; Log.set_level(level: String) -> Nil; Log.set_format(format: String) -> Nil

TOML — TOML.parse(text: String) -> Object; TOML.stringify(v: Object, sort_keys: Bool = false) -> String

Vector2 — Vector2.new(x, y); a.hash() -> Long; a.dot(b); a.length(); a.length_squared(); a.normalized(); a.distance_to(b); a.distance_squared_to(b) -> Float; to_string(a) -> String

Vector3 — Vector3.new(x, y, z); a.hash() -> Long; a.dot(b); a.length(); a.length_squared(); a.normalized(); a.distance_to(b); a.distance_squared_to(b) -> Float; to_string(a) -> String

Deque — Deque.new(); d.push_back(x); d.push_front(x); d.pop_back() -> Any; d.pop_front() -> Any; d.peek_back() -> Any; d.peek_front() -> Any; d.size(); d.empty(); d.to_array() -> Array; d.iter() -> Iterator; to_string(d)

PriorityQueue — PriorityQueue.new(*, key: Function | Nil = nil, reverse: Bool = false); pq.push(x); pq.pop() -> Any; pq.peek() -> Any; pq.size(); pq.empty(); to_string(pq) -> String

一覧ではなく章を読むもの

名前空間署名何をするもの
SQLite10組み込みSQLデータベース(query / execute / プリペアド文 / トランザクション)
Canvas71ゲーム向けイミディエイトモード2Dフレームバッファ(図形 / スプライト / オフスクリーン描画先 / テキスト / キー・マウス・ゲームパッド / ウィンドウ制御 / tone / 効果音 / music)
Scene195手続きジオメトリ向けのretained-mode 3Dレンダラ
Net22生のTCP / UDPソケットと名前解決(Httpの下位レイヤ)
Desktop / Webview10ネイティブWebViewのデスクトップアプリ: ローカルHTTPサーバ + ウィンドウを1呼び出しで
PEG18PEGパーサジェネレータ。文法を書くと構文木が返る
CodeGen144小さな言語のIRを手で組み立てて実行する
StateMachine8入れ子にできる状態機械。テキストでも書ける
FST14書き換えない辞書を圧縮して持つ。前方一致・補完・あいまい検索
Search9自分の文書を全文検索して順位をつける

5. テンプレート

CLI ツール

# doctest: skip
spec = {
  name: 'greet',
  args: [
    {name: 'who', doc: 'who to greet'},
    {name: 'times', type: 'Long', default: 1, doc: 'repeat count'},
  ],
}
args = Args.parse(Sys.argv, spec)
for _ in range(args.times) {
  println("hello, {args.who}")
}

HTTP リクエスト

# doctest: skip
r = Http.get('https://example.com/api', headers: {Accept: 'application/json'})
if !r.ok {
  throw "HTTP {r.status}: {r.reason}"
}
println(r.json().title)

ファイル処理

# doctest: skip
counts = File.with('input.txt', 'r', fn (f) {
  mut n = {}
  for line in f.lines() {
    for w in line.trim().lower().split(' ') {
      k = w.to_string()
      n[k] = n.get(k, 0) + 1
    }
  }
  n
})
for k in counts.keys().sorted() {
  println("{k}\t{counts[k]}")
}

テストファイル

# doctest: skip
# test_math.cul
@test
fn adds() {
  assert_eq(1 + 2, 3)
}

@parametrize([(1, 2, 3), (10, 20, 30)])
fn adds_each(a, b, want) {
  assert_eq(a + b, want)
}

次に読むもの