Skip to content

他の言語との対応

よく使う言語の書き方が Emela で何に当たるかを並べます。言語の全体を順に知りたいときは 言語ツアー から読んでください。

ほかの言語Emela
null / nil / NoneOption[A](Some(x) と None)
throw / try / catch、Result 型と ?error を宣言して fail、受けるのは escape
for / whileList.map、List.fold、再帰
再代入する変数同じ名前での束縛し直し、可変な状態は State
クラスとメソッド x.f()type とモジュールの関数、x |> f
interface で依存を渡す、DI、モックeffect、handler、with
リソースの後始末(defer、try-with-resources、Drop)ハンドラの init と release
async / await印なしの呼び出しと Fiber
文字列補間"Hello, #{name}"
型クラス / interfacetrait と impl(0.21 で入る予定)
TypeScriptEmela
type Shape = { kind: "circle"; r: number } | ... と switchenum Shape { Circle(r: Float) ... } と match
interface / 構造的な型type(名前で区別する)
null / undefined、?.、??Option[A]、Option.map、Option.or
throw / try / catchfail / escape。エラーは型に出る
async / await / Promise印なしの呼び出しと Fiber
let と再代入同じ名前での束縛し直し
`Hello, ${name}`"Hello, #{name}"
RustEmela
struct User { id: i32, name: String }type User(id: Int, name: String)
User { name, ..user }User(name:, ..user)
enum Shape { Circle { r: f64 }, Rect(f64, f64) }enum Shape { Circle(r: Float), Rect(Float, Float) }
match x { A => .., _ => .. }x match { A -> .. }
i32 / i64 / f64 / boolInt / Int64 / Float / Bool
Result<T, E> と ?-> T fails E。? は書かなくても伝わる
opt.ok_or(NotFound(id))?Option.or_fail(opt, NotFound(id:))
panic!、todo!()panic(..)、todo()
Dropハンドラの release
tokio::join! / select! / timeoutFiber.both / Fiber.race / Fiber.timeout
dbg!(x)dbg(x)

所有権、借用、ライフタイムはありません。値はすべて不変です。

GoEmela
type User struct { ID int; Name string }type User(id: Int, name: String)
func (u User) Greet() stringfn greet(u: User) -> String と u |> greet
(T, error) を返す-> T fails E
if err != nil { return err }何も書かない(自動で伝わる)
errors.As / errors.Isescape { NotFound(..) -> ... }
fmt.Errorf("...: %w", err)escape { A(..) -> fail B(..) }
defer conn.Close()ハンドラの release
go f()Fiber.fork(fn() { f() })(Fiber.scope の中だけ)
errgroup.GroupFiber.scope、Fiber.all、Fiber.map
context.WithTimeoutFiber.timeout
select で最初の1つFiber.race
チャネル一度だけの受け渡しは Deferred。キューは予定

goroutine と違い、fiber はスコープより長生きしません。キャンセルのために ctx を引数で回す必要もなく、Fiber.interrupt で止めるか、スコープの本体が値を返した時点で残っている fiber が中断されます。

Kotlin / JavaEmela
data class / recordtype User(id: Int, name: String)
user.copy(name = "bob")User(name: "bob", ..user)
sealed interface と実装enum とバリアント
when (x) { is A -> ... } / switch のパターンx match { A(..) -> ... }
User?、?.、?:、OptionalOption[User]、Option.map、Option.or
検査例外 throws NotFoundExceptionfails NotFound
RuntimeExceptiondefect(panic)
拡張関数 / static ユーティリティモジュールの関数と |>
コンストラクタ注入、Mockitoeffect / handler / with、テスト用のハンドラ
coroutineScope / StructuredTaskScopeFiber.scope
async と await()Fiber.fork と Fiber.join
withTimeoutOrNullFiber.timeout
@Test と assertEquals@test と assert

オーバーロードはありません。引数の数や型が違う関数には別の名前を付けます。

PythonEmela
@dataclass(frozen=True) class Usertype User(id: Int, name: String)
replace(user, name="bob")User(name: "bob", ..user)
match / case後置の x match { ... }
None / Optional[T]Option[T] の None
raise NotFound(id)fail NotFound(id:)
try / except NotFoundescape { NotFound(_) -> ... }
for x in xs / 内包表記List.map、List.filter、List.fold
x += 1x = x + 1(束縛し直し)
mock.patchハンドラの差し替え
asyncio.gather / TaskGroup / wait_forFiber.all / Fiber.scope / Fiber.timeout
print(x)(use Console).print_line(...)、デバッグなら dbg(x)
インデント{ }

型は静的に決まり、ほとんどは推論されます。dict と set に当たる Map と Set は 0.21 で入る予定です。

type User(id: Int, name: String)
fn greet(user: User) -> String {
"Hello, #{user.name}!"
}
fn main() {
count = 0
count = count + 1 # 再代入でなく、同じ名前で束縛し直す
sum = List.range(1, 11) |> List.fold(0, fn(acc, n) { acc + n })
first = List.get([10, 20, 30], 0)
user = User(id: 1, name: "alice")
dbg(user |> greet)
dbg("#{count} #{sum}")
dbg(first)
}
Terminal window
$ emela run main.emel
"Hello, alice!"
"1 55"
Some(10)

ループ構文がないので、繰り返しは List.fold のような関数か再帰で書きます。自分への末尾呼び出しはスタックを使いません。添字アクセス xs[0] もなく、List.get が Option を返します。

メソッド呼び出しの構文もありません。. で書けるのはフィールドの参照と、能力の値の操作((use Users).find(1))だけです。

use X は import とは別物で、エフェクト X の能力の値を取り出す式です。能力の値は、引数に渡す、データに入れる、返すのどれもできません。サービスを値として持ち回る書き方はできないので、使わせたいときはクロージャを渡します。main に残ってよいエフェクトはなく、使うものは main の中の with で全部供給します。標準のハンドラは Std.Platform の Platform にまとまっています。

文字列は + でつなげず、補間 "#{a}#{b}" を使います。数の型の間に暗黙の変換はなく、Int は32ビットです。Float.from_int や Int64.from_int で明示して変換します。

ほかに次の2つもよく引っかかります。

  • escape に _ の腕は書けません。受けるエラーを名前で並べます。
  • 名前の大文字と小文字に意味があります。変数と関数は snake_case、型・構成子・エフェクト・モジュールは PascalCase で、HTTP は型名に使えず Http と書きます。