Skip to content

Prelude と core

Prelude と core は import なしで使える標準ライブラリです。Prelude の名前は修飾なしで、core のモジュールの関数は List.map のようにモジュール名で修飾して呼びます。どれも純粋で、ホストに触れません。

fn main() {
words = String.split("emela is a functional language", " ")
dbg(List.length(words)) # 5
dbg(words |> List.filter(fn(w) { String.length(w) > 2 }))
dbg(Int.parse("42") escape { ParseError(..) -> 0 })
}

Prelude の名前はどのモジュールからも import なしで見えます。

分類名前
型Int Int64 Float Bool String Never List Option Ordering
構成子True False Some None Less Equal Greater
TraitEq Ord Show Hash
関数panic todo dbg
errorParseError

Prelude で宣言している enum は次の3つです。Bool は False を先に置いているので、False < True です。

pub enum Bool { False, True }
pub enum Option[A] { Some(A), None }
pub enum Ordering { Less, Equal, Greater }
宣言シグネチャ説明
panicpanic(message: String) -> Nevermessage を説明にした defect を起こす
todotodo() -> Neverまだ書いていない場所の印。not yet implemented の defect を起こす
dbgdbg[A: Show](value: A) -> A値の表示を標準エラーに1行出し、値をそのまま返す
ParseErrorerror ParseError(input: String)parse の入力の形が正しくない。input は入力の全体

dbg の出力は副作用ですが、デバッグ用の例外として use に出さず、純粋な関数として扱います。そのため式の途中に挟めます(xs |> dbg |> List.length)。defect は型に出ない異常終了で、emela run では終了コード 101 になります(エラー)。

32ビットの符号付き整数です。あふれは2の補数で巻き戻ります。

関数シグネチャ説明
to_stringto_string(n: Int) -> String10進の文字列。負なら - を付ける
parseparse(s: String) -> Int fails ParseError10進の数字と、先頭の - か + を読む。それ以外の文字(空白も)と範囲外は ParseError
checked_addchecked_add(a: Int, b: Int) -> Option[Int]和。あふれたら None
bit_andbit_and(a: Int, b: Int) -> Intビットごとの論理積
bit_orbit_or(a: Int, b: Int) -> Intビットごとの論理和
bit_xorbit_xor(a: Int, b: Int) -> Intビットごとの排他的論理和
bit_notbit_not(n: Int) -> Intビットの反転。-n - 1 と同じ
shift_leftshift_left(n: Int, by: Int) -> Int左シフト。by が 32 以上なら 0
shift_rightshift_right(n: Int, by: Int) -> Int算術右シフト(上位を符号で埋める)。by が 32 以上なら 0 か -1
shift_right_unsignedshift_right_unsigned(n: Int, by: Int) -> Int論理右シフト(上位を 0 で埋める)。by が 32 以上なら 0

シフト量が負なら defect です。ビット演算の演算子はなく、関数をパイプでつなぎます(flags |> Int.bit_and(MASK))。

const READ: Int = 4
const WRITE: Int = 2
fn main() {
dbg(2147483647 + 1) # -2147483648(巻き戻る)
dbg(Int.checked_add(2147483647, 1)) # None
dbg(Int.bit_or(READ, WRITE) |> Int.bit_and(WRITE)) # 2
dbg(Int.parse(" 42") escape { ParseError(input:) -> String.length(input) }) # 3
}

64ビットの符号付き整数です。あふれは Int と同じく巻き戻ります。整数のリテラルは、文脈が Int64 を求めれば Int64 になります。

関数シグネチャ説明
from_intfrom_int(n: Int) -> Int64Int から作る
to_intto_int(n: Int64) -> Int下位32ビットを残す(巻き戻る)
to_stringto_string(n: Int64) -> String10進の文字列。接尾辞は付けない
bit_andbit_and(a: Int64, b: Int64) -> Int64ビットごとの論理積
bit_orbit_or(a: Int64, b: Int64) -> Int64ビットごとの論理和
bit_xorbit_xor(a: Int64, b: Int64) -> Int64ビットごとの排他的論理和
bit_notbit_not(n: Int64) -> Int64ビットの反転
shift_leftshift_left(n: Int64, by: Int) -> Int64左シフト。by が 64 以上なら 0、負なら defect
shift_rightshift_right(n: Int64, by: Int) -> Int64算術右シフト。by が 64 以上なら 0 か -1、負なら defect
shift_right_unsignedshift_right_unsigned(n: Int64, by: Int) -> Int64論理右シフト。by が 64 以上なら 0、負なら defect
fn main() {
big = Int64.from_int(2147483647) + Int64.from_int(1)
dbg(Int64.to_string(big)) # "2147483648"
dbg(Int64.to_int(big)) # -2147483648(下位32ビット)
}

IEEE 754 の倍精度(binary64)です。

関数シグネチャ説明
from_intfrom_int(n: Int) -> FloatFloat にする。どの Int も正確に表せる
parseparse(s: String) -> Float fails ParseErrorto_string が書く形(1.5、1e+21、Infinity、-Infinity、NaN、先頭の符号)を読む。それ以外は ParseError
to_stringto_string(x: Float) -> String往復できる最短の桁。整数の値なら .0 を付ける(1.0)
floorfloor(x: Float) -> Int負の無限大の方向へ丸める
ceilceil(x: Float) -> Int正の無限大の方向へ丸める
roundround(x: Float) -> Int最も近い整数。ちょうど半分は 0 から遠い方へ(2.5 は 3、-2.5 は -3)
truncatetruncate(x: Float) -> Int0 の方向へ丸める
  • Int への丸めの結果が Int の範囲外か、NaN、±Infinity なら defect です。巻き戻しも飽和もしません。
  • Float のゼロ除算は IEEE のとおり無限大か NaN になり、defect になりません。% は fmod で、符号は被除数に従います。
  • Int と Float は暗黙に変換されません。Float.from_int と丸めの関数で明示します。
fn average(xs: List[Int]) -> Float {
sum = List.fold(xs, from: 0, f: fn(acc, x) { acc + x })
Float.from_int(sum) / Float.from_int(List.length(xs))
}
fn main() {
dbg(average([1, 2, 4])) # 2.3333333333333335
dbg(Float.round(2.5)) # 3
dbg(Float.truncate(-2.7)) # -2
dbg(Float.to_string(0.1 + 0.2)) # "0.30000000000000004"
}

Unicode 文字の列です。内部の符号化は観測できません。文字の型はなく、1文字は長さ1の String です。位置を数値で指定する関数はありません。走査は chars や split で書きます。

関数シグネチャ説明
lengthlength(s: String) -> Int見た目の文字の数(書記素クラスタの数)。length("👍") は 1
byte_sizebyte_size(s: String) -> IntUTF-8 にしたときのバイト数
appendappend(a: String, b: String) -> String2つをつなぐ
concatconcat(parts: List[String]) -> String全部を順につなぐ。join(parts, "") と同じ
joinjoin(parts: List[String], sep: String) -> Stringsep を挟んでつなぐ
containscontains(s: String, sub: String) -> Boolsub を含むか
starts_withstarts_with(s: String, prefix: String) -> Boolprefix で始まるか
ends_withends_with(s: String, suffix: String) -> Boolsuffix で終わるか
splitsplit(s: String, sep: String) -> List[String]sep で分ける。sep が空なら書記素クラスタごと
charschars(s: String) -> List[String]書記素クラスタごとのリスト
trimtrim(s: String) -> String前後の空白(Unicode の White_Space)を取り除く
code_pointscode_points(s: String) -> List[Int]コードポイントの値のリスト
from_code_pointfrom_code_point(n: Int) -> Option[String]コードポイント1つの文字列。n が Unicode のスカラー値(0〜10FFFF でサロゲートを除く)でなければ None
  • split は join(split(s, sep), sep) == s が成り立つように分けます。空の要素も残します(split("a,,b", ",") は ["a", "", "b"])。split("", "") は []、split("", ",") は [""] です。
  • contains、starts_with、ends_with、split はコードポイントの列で照合し、書記素の境界は見ません。
  • trim が取り除くのは White_Space 特性の25文字で、表は固定です。U+FEFF(BOM)と U+200B は含みません。
  • 文字列の大小比較(<)はコードポイントの辞書順です。
  • 書記素クラスタの境界は Unicode の版で変わります。ターゲットの間で結果をそろえるために、使う Unicode の版を固定する予定です(版はまだ決まっていません)。
fn initials(name: String) -> String {
String.split(String.trim(name), " ")
|> List.filter(fn(w) { w != "" })
|> List.map(fn(w) { Option.or(List.head(String.chars(w)), "") })
|> String.concat
}
fn main() {
dbg(initials(" Ada Lovelace ")) # "AL"
dbg(String.length("👍")) # 1
dbg(String.byte_size("👍")) # 4
dbg(String.split("a,b,,c", ",")) # ["a", "b", "", "c"]
dbg(String.code_points("A")) # [65]
}

不変の単方向リストです。リストを最初の引数 xs に取ります。長いリストでもスタックを使い切らないよう、ループは自分への末尾呼び出しで書かれています。

関数シグネチャ説明
mapmap[A, B](xs: List[A], f: fn(A) -> B) -> List[B]各要素に f を適用する
filterfilter[A](xs: List[A], f: fn(A) -> Bool) -> List[A]f が True の要素だけを、順を保って残す
foldfold[A, B](xs: List[A], from: B, f: fn(B, A) -> B) -> B先頭から畳み込む。f(f(f(from, x0), x1), x2)
lengthlength[A](xs: List[A]) -> Int要素の数
is_emptyis_empty[A](xs: List[A]) -> Bool空か
getget[A](xs: List[A], index: Int) -> Option[A]index 番目(0 始まり)。範囲外と負は None
headhead[A](xs: List[A]) -> Option[A]先頭の要素。空なら None
tailtail[A](xs: List[A]) -> List[A]先頭を除いた残り。空なら空
reversereverse[A](xs: List[A]) -> List[A]逆順
appendappend[A](xs: List[A], ys: List[A]) -> List[A]2つのリストをつなぐ
flattenflatten[A](xss: List[List[A]]) -> List[A]リストのリストを、順を保って平らにする
partitionpartition[A](xs: List[A], f: fn(A) -> Bool) -> (List[A], List[A])f が True の要素と False の要素に、順を保って分ける
anyany[A](xs: List[A], f: fn(A) -> Bool) -> BoolTrue になる要素があるか。見つけたら止める。空なら False
allall[A](xs: List[A], f: fn(A) -> Bool) -> Boolすべて True か。False で止める。空なら True
findfind[A](xs: List[A], f: fn(A) -> Bool) -> Option[A]True になる最初の要素
rangerange(from: Int, to: Int) -> List[Int]from 以上 to 未満の整数を昇順に。to <= from なら空
zipzip[A, B](xs: List[A], ys: List[B]) -> List[(A, B)]同じ位置の要素の組。短い方に合わせる
fn main() {
(evens, odds) = List.range(1, 7) |> List.partition(fn(x) { x % 2 == 0 })
dbg(evens) # [2, 4, 6]
dbg(odds) # [1, 3, 5]
dbg(List.zip(evens, ["a", "b"])) # [(2, "a"), (4, "b")]
dbg(List.get(evens, 5)) # None
dbg(List.fold(odds, from: 0, f: fn(acc, x) { acc + x })) # 9
}

値があるか、ないかを表す Prelude の enum Option[A] の関数です。Option を最初の引数 opt に取ります。

関数シグネチャ説明
mapmap[A, B](opt: Option[A], f: fn(A) -> B) -> Option[B]中身に f を適用する
and_thenand_then[A, B](opt: Option[A], f: fn(A) -> Option[B]) -> Option[B]Option を返す f を適用して平らにする
oror[A](opt: Option[A], default: A) -> A中身か、None なら default
is_someis_some[A](opt: Option[A]) -> BoolSome か
is_noneis_none[A](opt: Option[A]) -> BoolNone か
or_failor_fail[A, E](opt: Option[A], err: E) -> A fails E中身か、None なら err で fail する

or_fail は Option をエラーに変えます。逆に、エラーを Option に変えるときは escape を使います。

error MissingName(key: String)
fn lookup(pairs: List[(String, String)], key: String) -> Option[String] {
List.find(pairs, fn(p) {
(k, _) = p
k == key
}) |> Option.map(fn(p) {
(_, v) = p
v
})
}
fn main() {
pairs = [("name", "emela"), ("version", "0.20")]
dbg(lookup(pairs, "name") |> Option.or("unknown")) # "emela"
dbg(lookup(pairs, "license") |> Option.is_none) # True
name = lookup(pairs, "author") |> Option.or_fail(MissingName(key: "author")) escape {
MissingName(key:) -> "no #{key}"
}
dbg(name) # "no author"
}

Bool は Prelude の enum で、0.20 には Bool.to_string のように呼べる関数のモジュールがありません。否定は !、論理積と論理和は && と || で、どちらも右辺を必要なときだけ評価します。文字列にするときは補間("#{flag}")を使います。

fn main() {
ready = True
dbg(!ready || List.is_empty([1])) # False
dbg("ready: #{ready}") # "ready: True"
}
項目規則
Int の範囲-2,147,483,648 から 2,147,483,647
Int64 の範囲-2^63 から 2^63 - 1
整数のあふれ2の補数で巻き戻る。defect にはならない(-2147483648 / -1 も巻き戻る)
整数の除算0 の方向へ切り捨てる。剰余の符号は被除数に従う(-7 / 2 は -3、-7 % 2 は -1)
整数のゼロ除算defect
Float のゼロ除算IEEE のとおり無限大か NaN
型の変換暗黙には行わない

あふれを検出したいときは Int.checked_add を使います。数値の意味はどのターゲットでも同じです。詳しくは ターゲット にあります。