Docsの目次

文法の早見表

目次 · 初めて書くなら:入門ガイド · 関数を調べる:組み込み関数

このページはHigh(.nagi)の書式を引くための資料です。字下げと型付きの関数・値を使ってアプリを書く構文です。短い例は関数内に書く断片も含みます。各例をまとめて動かすには入門の完成コードを使ってください。

Pythonでよく書く処理から探すなら、次の入口を使ってください。

やりたいこと Pythonでの書き方 現在のNagiで学ぶ場所
値を更新する count += 1 値と型。再代入でも型は同じ
関数を使う def add(a, b): 関数。引数型を指定し、実行はmainから
リストへ追加する items.append(value) 配列と反復。append(items, value)
渡した値を後でも使う b = aで同じ値を参照 viewとcopy、共有
値がない場合を分ける if value is None: Some/Noneのmatch
失敗をその場で扱う try/except Resultのmatch。try 式はErrの伝播
非同期処理を待つ await operation() await、spawnとscope
このページの目次

ファイルと字下げ

  • UTF-8で保存し、拡張子を.nagiにする。
  • トップレベルにはimport・from、class、enum、def、async def、Rustの外部関数宣言を書く。実行する文は関数内に書く。
  • ブロックの前に:を付け、空白で字下げする。空白4つを推奨する。タブは禁止。
  • コメントは#。識別子は英字・数字・_で、数字からは始めない。日本語の文字列・コメントは使える。
  • ()や[]の中は複数行に分けられる。引数や要素の末尾の余分な,は未対応。
def main():
    # コメント
    print("Hello, Nagi!")

値と変数

書き方 意味
count = 10 型を推論する。整数の標準はi64
count: i32 = 10 型を指定する
rate = 1.5 小数の標準はf64
enabled = True / False 真偽値。true / falseも受け付ける
name = "Nagi" / 'Nagi' UTF-8文字列
values = [1, 2, 3] 同じ型の要素の配列
values: List[i64] = [] 空配列には型注釈を付ける
missing: i64? = None 値がないnullable。nullも受け付ける
present: i64? = some(42) 値があるnullable
count = 11 同じ型で再代入
count += 1 / -= 1 / *= 2 複合代入。/= / %=は未対応

変数へ別の型の値を再代入することはできません。明示moveの移行では、既存の非Copyローカルの代入にstd.ownership.moveを使います。新値生成とCopy代入には不要です。実装・収録版は代入の規則を参照してください。

文字列のエスケープは\n、\r、\t、\"、\'、\\です。文字列の+による連結、f-string、文字列の補間、三重引用符は未対応です。型の一覧は型を参照してください。

関数とreturn

呼び出し先は、同名のローカル変数に入れた関数、自作関数、組み込み関数の順に決まります。自作のlenを定義すると、len(...)はその関数を呼びます。関数以外の変数は呼び出せません。

typeなどRust側の予約語も、Nagiの文法で使える位置なら名前にできます。生成Rustで必要な名前の変換はコンパイラが行い、Low・JSON・SQLiteの項目名には元の名前を残します。

def add(a: i64, b: i64) -> i64:
    return a + b

def show(value: i64):
    print(value)
    return

引数の型は必須です。戻り値を省略するとunit。値を返す関数は全経路で戻り値が必要です。呼び出しはadd(1, 2)のように位置引数を使います。デフォルト引数、可変長引数、ユーザー定義generic関数はありません。

分岐とループ

次は関数内の断片です。

score = 80
if score >= 80:
    print("合格")
else:
    print("再挑戦")

for index in range(3):
    print(index)

count = 0
while count < 3:
    count += 1

条件はboolです。range(n)は0以上n未満で、引数は1つです。配列やviewのforは、Copy要素を値で読み、非Copy要素を読み取り専用で借ります。所有権で制約を確認できます。elif、break、continue、passはありません。

演算子

優先順位が高いものから並べています。同じ段の二項演算は左から評価する形で解析します。曖昧な式には()を付けてください。

優先順位 演算子 例
高 呼び出し、フィールド、index add(1, 2)、point.x、values[0]
↓ -(単項)、not、try、await -count、not enabled、try await sqlite.open(path, config)
↓ *、/、% count * 2
↓ +、- count + 1
↓ <、>、<=、>= count < 10
↓ ==、!= count == 10
↓ and count > 0 and count < 10
低 or enabled or count == 0

符号反転の-valueは、符号付き整数(i8 / i16 / i32 / i64)と浮動小数点数(f32 / f64)に使えます。関数やclassの値には使えません。

比較する値 == / != < / > / <= / >=
同じ型の数値・bool・str 対応 対応
UUID・timestamp 対応 未対応
view[str]・view[bytes] 対応 対応
view[T] 要素Tが一致比較に対応する場合 要素Tが大小比較に対応する場合
Method・Status 対応。Methodは借用して比較する 未対応。Statusの数値は.valueで比較する
class・所有するList 未対応 未対応

classの配列を借りたview[Point]も、そのまま比較できません。必要なフィールドを取り出して比較してください。配列のviewは、要素ごとの比較になります。

異なる数値型は暗黙に変換しません。i32からi64にはi64(value)を使います。i32(i64値)はResult[i32, Error]を返すため、Result関数内でtry i32(value)などと書きます。

比較の連鎖0 < count < 10は使わず、count > 0 and count < 10と書きます。整数の/は整数除算です。**、//、ビット演算は未対応です。

class、配列、view

class Point:
    x: f64
    y: f64

def main():
    point = Point(x=1.0, y=2.0)
    print(point.x)
    values = [10, 20]
    append(values, 30)
    print(values[0])
    borrowed = view(values)
    duplicate = copy(borrowed)
    print(len(duplicate))

classは全フィールドを名前付きで指定します。フィールド・indexへの代入、method、継承は未対応です。indexは0からで、負数や範囲外は実行時panicになります。文字列のindexは使えません。文字列のlenはUTF-8のbyte数で、Pythonのlen(str)の文字数とは違います。len("あ")は3です。配列のlenは要素数です。配列や文字列の区間を借りる場合はtry slice(view(data), start, end)です。

自作関数への所有文字列・配列の引き渡しはmoveです。読むだけなら引数をview[str]やview[i64]にし、view(value)で渡します。詳細は入門ガイドと所有権を参照してください。

Result、async、scope

書き方 意味
-> Result[i64, Error] 成功なら整数、失敗ならErrorを返す
return ok(42) 成功を返す
return error("理由") 失敗を返す
return not_found("理由") 対象なしを返す。HTTPでは404
return fail(problem) Error・独自class・enumの失敗値を返す
value = try parse_i64("42") 値を取り出す。失敗なら呼び出し元へ返す
async def work(): 非同期関数を定義する
await sleep(10) 非同期処理を待つ。単位はミリ秒
pool = try await sqlite.open(":memory:", config) 非同期処理を待ち、Resultの失敗も伝える

tryは同じエラー型のResultを返す関数内、awaitはasync関数内で使います。tryはPythonの例外捕捉ではなく、成功値を取り出し、Errを呼び出し元へ返します。その場でResultの成功・失敗を処理する場合は、次のように両方のcaseを書きます。これは関数内の断片です。

match parse_i64("42"):
    case Ok(number):
        print(number)
    case Err(problem):
        print(error_kind(problem))

値がない場合のT?(Option)は処理のErrやpanicとは別です。case Some(value):とcase None:の両方を書いて取り出します。不在の実行例も参照してください。Some(_)で値を捨てられます。

使わないpayloadは_にします。matchは対象の所有値を消費し、payloadの名前はcase内だけで使えます。外側の変数と同じ名前は使えません。Result[T, E]のEには独自class・enumも使えます。enumはcase Choice.Cancelled:やcase Choice.Selected(id):で全種類を処理します。型の定義とエラー処理を参照してください。

子taskは次の完全なコードのようにscope内でspawnします。

async def main() -> Result[unit, Error]:
    async with scope:
        spawn sleep(10)
        spawn sleep(15)
    return ok(print("完了"))

このコードは両方のsleepが終了してから完了を表示します。通常のscope終了では子taskを待ちます。旧statement spawnでは本体終了後に子のErrやpanicを検出すると、残りの子を止めて待ちます。親Futureの破棄や本体panicでは停止要求と終了確認を区別します。scope内のreturnとviewを別taskへ渡すことは未対応です。旧statement spawnはunitかResult[unit, Error]を返すasync呼出しに限ります。Task結果handleはNagi 0.1.11で公開済みです。Task bindingはawait時に子の結果をscope body内で受け取ります。受取failureを処理してもscope faultは残り、scope出口では子の実joinを待ちます。利用するcompilerが0.1.11以降であることを確認してください。詳細はasyncを参照してください。

import、HTTP、Rust

用途 書き方 詳細
ファイルを読み込む import "models.nagi" import。同じ名前空間に読み込む
module名を付ける import "orders.nagi" as orders orders.Orderやorders.score(...)で、そのファイル自身の定義を使う
定義を選ぶ from "orders.nagi" import Order as SavedOrder ,で複数の定義を選べる。各as 名前は省略できる
標準HTTPを読み込む import std.http.server as http http.Requestやhttp.Status.OKを使う。asは必須
標準型を選ぶ from std.http.server import Request, Response, Status as Code 標準import
GET handlerを定義する 関数の前に@get("/users/{id}") HTTP。@post、@put、@deleteもある
HTTP公開方針 http.public_policy[State]() 全routeへ明示Policy。active HTMLはSF04まで未対応
テキストを埋め込む include_text("index.html") ソースの場所を基準に、コンパイル時に埋め込む
Rust関数を宣言する @rust("native::crc32")の次行にextern def crc32(text: view[str]) -> i64 Rust連携。本体・末尾の:は不要

orders.OrderとSavedOrderは同じ型です。別ファイルの同名classは別の型として扱います。from・asはimport文だけのキーワードで、既存の識別子としても使えます。

Pythonに似ていても違うところ

Pythonでよく書く形 Nagiでは
def add(a, b): 引数型を書く。戻り値があるなら-> 型も書く
print(a, b) 1引数ずつprint(a)、print(b)
items.append(x) append(items, x)
try: ... except: try 式で失敗を伝える、またはmatchでResultを分岐する
from models import User from "models.nagi" import User。ファイルの相対パスを引用符で囲む
辞書、tuple、内包表記、lambda 未対応。class、配列、通常の関数・ループを使う
str(42)、任意型へのcast 汎用変換は未対応。直接print(42)などを使う

nullableはNone / some(value)で作り、case Some(value): / case None:のmatchで取り出します。汎用unwrap APIはありません。

整数のrelease演算はRust backendの固定幅演算に従い、加算などのoverflowはwrapします。debug Rust側ではpanicする場合があります。checked / wrapping演算を言語として統一することは今後の課題です。Lowの構文・差し替えはLow、実装予定はroadmapにあります。

Nagi 0.2.0 · Docsこのページのソース