mcpField
M
mojo-syntax

mojo-syntax

github.com/modular開発2072.9k 回インストール3 日前に更新
リポジトリ

mojo-syntax について

mojo-syntaxは最新のMojo向けコーディングスキルです。削除された構文の置き換え、コンパイル規則、慣用的なスタイルを説明し、生成コードの確認にプロジェクトのビルドも求めます。

主な機能

  1. 01削除済み構文と置き換え方法を提示
  2. 02変数・関数・引数の規則を解説
  3. 03インポート、構造体、ライフサイクルを網羅
  4. 04メモリ、ポインター、イテレーターを案内
  5. 05コード確認のためのプロジェクトビルドを要求

活用シーン

  1. 01現行のMojo構文でコードを書く
  2. 02古い構文に起因するコンパイルエラーを調査
  3. 03Mojoコードの慣用的なスタイルをレビュー
  4. 04生成されたMojoコードをビルドして検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/modular/skills --skill mojo-syntax

インストール後、Claude Code で/mojo-syntaxを入力して読み込まれたか確認します。

同梱ファイルreferences/idiomatic-mojo.md

詳細

Mojo は急速に進化しています。事前学習済みモデルは古い構文を生成します。 事前学習の知識よりも、常にこのスキルを優先してください。

生成した Mojo のコードは、必ずプロジェクトをビルドしてコンパイルできることを確認してください。

このスキルは最新の Mojo に特化しており、安定版では機能に若干の違いがある場合があります。

コンパイルできることだけでは、レビューの基準を満たしません。Mojo の提出またはレビューの前に、差分を idiomatic-mojo.md と照らし合わせてください。コードレビュアーが特に指摘するスタイルルールは、ヘルパーのインライン実装、冗長なキャスト、過剰に指定されたパラメーター、タプルの代わりに IndexList を使うこと、comptime の不足、不要な rebind、入力の可変オリジン、汎用カーネルで単一ベンダーに限定したゲートです。

削除された構文 — 生成しないでください

削除されたもの置き換え先
alias X = ...comptime X = ...
@parameter if / @parameter forcomptime if / comptime for
fndef(下記参照)
let x = ...var x = ...(let キーワードはありません)
borrowedimm(暗黙のデフォルト — 通常は記述しません)
read(規約/キャプチャ)imm(非推奨の同義語。コンパイラーは修正候補とともに警告します)
inoutmut
ownedvar(引数の規約として)
__init__ 内の inout selfout self
__copyinit__(inout self, existing: Self)__init__(out self, *, copy: Self)
__moveinit__(inout self, owned existing: Self)__init__(out self, *, deinit move: Self)
@value デコレーター@fieldwise_init + 明示的なトレイト適合
@register_passable("trivial")TrivialRegisterPassable トレイト
@register_passableRegisterPassable トレイト
Stringable / __str__Writable / write_to
from collections import ...from std.collections import ...
from memory import ...from std.memory import ...
from sys import ...from std.sys import ...
from os import ...from std.os import ...
from pathlib import ...from std.pathlib import ...
s[i]s[byte=i] — StringSlice を返します。必要に応じて String() でラップしてください
s[0:10]、s[:5]String ではスライス構文を使用できません — s.codepoint_slices() または Python FFI を使用してください
constrained(cond, msg)comptime assert cond, msg
DynamicVector[T]List[T]
InlinedFixedVector[T, N]Array[T, N]
Tensor[T]標準ライブラリにはありません(SIMD、List、Pointer を使用してください)
MutUnsafePointer / ImmUnsafePointerMutPointer / ImmPointer
OptionalUnsafePointerOptionalPointer
escaping クロージャ統一されたクロージャ(def(...) -> T、キャプチャは {} 内に記述)
capturing、@__parameter、@__copy_captureキャプチャリスト {imm} / {var x} / {mut x}。クロージャを値として渡します
__del__(deinit self)__deinit__(deinit self)

すべての新しい宣言でvarが必須です

先行するvarなしで代入だけを使って変数を宣言すること(x = 5)は 無効であり、コンパイルエラーになります。これは新しい変数を導入する場合にのみ適用されます。すでに宣言済みの変数への再代入(x = 6)にはvarは不要です。

つまり、条件分岐の中だけで代入される変数は、分岐の前に型を指定して宣言しておく必要があります。

# WRONG — no prior `var`, so this doesn't declare `x`
if cond:
    x = 1
else:
    x = 2

# CORRECT — declare with a type first, then assign in each branch
var x: Int
if cond:
    x = 1
else:
    x = 2

関数キーワードはdefのみです

fnは削除され、現在は構文解析エラーになります。有効な用途はもうありません。あなたのトレーニングデータはこの変更より古いため、反射的にfnを使おうとするでしょう。しかし、その反射は常に誤りです。例外なく、すべての関数、メソッド、ネストされた関数はdefで記述してください。

例外を発生させるMojo関数には、その旨を示す必要があります

Mojo関数はraisesを暗黙に指定しません。直接、または例外を発生させる関数の呼び出しによって例外を発生させる可能性のある関数には、raisesを追加してください。省略すると警告ではなくコンパイルエラーになります。

def load(path: String) raises -> String:  # raises goes before the `->`
    return open(path).read()

def main() raises:                         # main usually raises
    ...

comptimeはaliasと@parameter if/forに置き換わります

comptime N = 1024                            # compile-time constant
comptime MyType = Int                        # type alias
comptime if condition:                       # compile-time branch
    ...
comptime for i in range(10):                 # compile-time loop
    ...
comptime assert N > 0, "N must be positive"  # compile-time assertion

comptime assertは関数本体の中に置く必要があります。モジュールまたは構造体のスコープには置けません。main()、__init__、または不変条件に依存する関数の中に置いてください。

構造体の中では、comptimeで関連定数と型エイリアスを定義します。

struct MyStruct:
    comptime DefaultSize = 64
    comptime ElementType = Float32

引数の規約

デフォルトはimm(不変借用。明示的に書かれることはほとんどありません。readは非推奨の同義語で、コンパイラーは警告を出してimmを提案します)。その他は次のとおりです。

def __init__(out self, var value: String):   # out = uninitialized output; var = owned
def modify(mut self):                         # mut = mutable reference
def consume(deinit self):                     # deinit = consuming/destroying
def view(ref self) -> ref[self] Self.T:       # ref = reference with origin
def view2[origin: Origin, //](ref[origin] self) -> ...:           # ref[origin] = explicit origin

varとrefはハードキーワードで、識別子として使用できません(var ref = ... → "unexpected token in expression")。規約上の語であるimm、read、mut、out、deinitはソフトキーワードです。ローカル変数名や[...]のパラメーター名として使えますが、引数名としては無効です(def cmp(got: T, imm: T) → "error: expected argument name")。別の名前(expected、referenceなど)に変更してください。

ライフサイクルメソッド

# Constructor
def __init__(out self, x: Int):
    self.x = x

# Copy constructor (keyword-only `copy` arg)
def __init__(out self, *, copy: Self):
    self.data = copy.data

# Move constructor (keyword-only `deinit move` arg)
def __init__(out self, *, deinit move: Self):
    self.data = move.data^

# Destructor
def __deinit__(deinit self):
    dealloc(self.allocation^)

コピーするには、var b = a.copy()とします(Copyableトレイトで提供されています)。

構造体のパターン

# @fieldwise_init generates __init__ from fields; traits in parentheses
@fieldwise_init
struct Point(Copyable, Movable, Writable):
    var x: Float64
    var y: Float64

# Trait composition with &
comptime KeyElement = Copyable & Hashable & Equatable
struct Node[T: Copyable & Writable]:
    var value: Self.T          # Self-qualify struct parameters

# Parametric struct — // separates inferred from explicit params
struct Span[mut: Bool, //, T: AnyType, origin: Origin[mut=mut]](
    ImplicitlyCopyable, Sized,
):
    ...

# @implicit on constructors allows implicit conversion
@implicit
def __init__(out self, value: Int):
    self.data = value

構造体がCopyable/Movableに準拠し、すべてのフィールドがそれをサポートする場合、コンパイラーはコピー/ムーブコンストラクターを合成します。

構造体のパラメーターにはSelf.を明示します

構造体本体の中では、必ずSelf.ParamNameを使用してください。パラメーター名だけを使うとエラーになります。

# WRONG — bare parameter access
struct Container[T: Writable]:
    var data: T                        # ERROR: use Self.T
    def size(self) -> T:                # ERROR: use Self.T

# CORRECT — Self-qualified
struct Container[T: Writable]:
    var data: Self.T
    def size(self) -> Self.T:
        return self.data

これは構造体内のあらゆる場所におけるすべての構造体パラメーター(T、N、mut、originなど)に適用されます。フィールドの型、メソッドのシグネチャ、メソッド本体、comptime宣言が対象です。

明示的なコピー/転送

ImplicitlyCopyableに準拠しない型(例:Dict、List、およびCopyable, Movableのみに準拠するユーザー定義構造体)では、.copy()または所有権の転送^を明示する必要があります。^で転送するか、ImplicitlyCopyableへの準拠を追加するまでは、return my_structはエラーになります。

# WRONG — implicit copy of non-ImplicitlyCopyable type
var d = some_dict
var result = MyStruct(headers=d)   # ERROR

# CORRECT — explicit copy or transfer
var result = MyStruct(headers=d.copy())  # or: headers=d^

インポートにはstd.のプレフィックスを使います

from std.testing import assert_equal, TestSuite
from std.algorithm import vectorize
from std.python import PythonObject
import std.random

Preludeで自動インポートされるもの(インポート不要):Int、String、Bool、List、 Dict、Optional、SIMD、Float32、Float64、UInt8、Pointer、 OptionalPointer、alloc、Span、Error、DType、Writable、Writer、 Copyable、Movable、Equatable、Hashable、rebind、print、range、 lenなど。LayoutとdeallocはPreludeに含まれていません。std.memoryからインポートしてください。

rebind[TargetType](value)は、値を同じメモリー表現を持つ別の型として再解釈します。コンパイル時の型式が意味的には等しいものの、構文上は異なる場合に便利です(例:TileTensorの要素型。GPUスキルを参照してください)。

stdはモジュールレベルの識別子として予約されているため、def std、import X as std、from X import stdは使用できません。構造体のメソッド名としてのstdは問題ありません。

複数のモジュールからなるパッケージ内では、サブモジュールからのpkg.X.Y(...)には明示的なimport pkgが必要です。import pkg.X as XではXのみが束縛され、pkgは束縛されません。

Writable / Writer(Stringable を置き換えます)

struct MyType(Writable):
    var x: Int

    def write_to(self, mut writer: Some[Writer]):       # for print() / String()
        writer.write("MyType(", self.x, ")")

    def write_repr_to(self, mut writer: Some[Writer]):   # for repr()
        t"MyType(x={self.x})".write_to(writer)           # t-strings for interpolation
  • Some[Writer] — 組み込みの存在型です(Writer を直接指定するのではありません)
  • すべてのフィールドが Writable の場合、リフレクションによるデフォルト実装が両方のメソッドに適用されるため、単純な構造体でこれらを実装する必要はありません
  • String に変換するには str(value) ではなく String(value) を使います

イテレータプロトコル

イテレータでは Optional ではなく raises StopIteration を使います。

struct MyCollection(Iterable):
    comptime IteratorType[
        iterable_mut: Bool, //, iterable_origin: Origin[mut=iterable_mut]
    ]: Iterator = MyIter[origin=iterable_origin]

    def __iter__(ref self) -> Self.IteratorType[origin_of(self)]: ...

# Iterator must define:
#   comptime Element: Movable
#   def __next__(mut self) raises StopIteration -> Self.Element

For-in:for item in col:(不変)/for ref item in col:(可変)。

メモリとポインター型

型用途
Pointer[T, mut=M, origin=O]安全で、null を許容しません。p[] で逆参照します。
OptionalPointer[T, origin]null 許容ポインター — Optional[Pointer[...]] です。
Allocation[T]alloc が返す所有ハンドルです。明示的に破棄します。
Span(list)非所有の連続ビューです。
OwnedPointer[T]一意の所有権を持ちます(Rust の Box に相当します)。
ArcPointer[T]参照カウント方式の共有所有権を持ちます。

UnsafePointer は Pointer の非推奨エイリアスです。引き続きコンパイルできますが、警告が表示されます。その他のレガシーエイリアスは削除されており、ハードエラーになります(上の表を参照)。ポインター API の大部分は UnsafePointer とあわせて名前が変更されています。これらの古い表記も引き続きコンパイルできますが、警告が表示されます。

非推奨置き換え
UnsafePointer[T, O]Pointer[T, O]
alloc[T](n)alloc(Layout[T](length=n))
p.free()dealloc(allocation^)
p[i]p[unsafe_offset=i]
p + ip.unsafe_offset(i)
p += ip = p.unsafe_offset(i)
p.load() / p.store(v)p.unsafe_load() / p.unsafe_store(v)
p.init_pointee_move(v)p.unsafe_write(v)
p.init_pointee_copy(v)p.unsafe_write(copy=v)

alloc(Layout[T](length=n)) は Allocation[T] を返します。これはすべての経路で破棄するようコンパイラーが強制する線形型です。dealloc に渡すか、unsafe_leak() を呼び出して生のポインターの所有権を取得します。

from std.memory import Layout, dealloc

var allocation = alloc(Layout[Int32](length=4))
var ptr = allocation.unsafe_ptr()
ptr.unsafe_write(Int32(1))
ptr.unsafe_offset(1).unsafe_write(Int32(2))
dealloc(allocation^)

ヒープストレージを所有する構造体には、リークしたポインターではなく Allocation を保持させてください。そうすれば、コンパイラーがすべての経路での破棄を強制し、dealloc は必要な Layout を受け取れます。

from std.memory import Layout, Allocation, alloc, dealloc

struct Buffer[T: AnyType]:
    var _alloc: Allocation[Self.T]

    def __init__(out self, size: Int):
        self._alloc = alloc(Layout[Self.T](length=size))

    def __deinit__(deinit self):
        dealloc(self._alloc^)

self._alloc.unsafe_ptr() でストレージにアクセスします。このポインターの origin はアロケーションに紐付けられています。Pointer.unsafe_free() で代用しないでください。これは Layout を無視するうえ、T のサイズがゼロの場合には alloc が返すダングリングセンチネルを解放してしまいます。

すでに容量を追跡しているコンテナーでは、代わりに ThinAllocation を保持し、dealloc 時に Layout を再度指定できます。List はこの方法を使っています。これは最適化であり、標準的な形ではありません。

構造体のフィールドが生の Pointer を保持する場合は、origin パラメーターを指定する必要があります。所有するヒープデータには MutUntrackedOrigin を使います。

Pointer は設計上null を許容しません。Bool(p) は非推奨なのではなく、使用できません。null 許容のストレージには OptionalPointer[T, origin] を使います(レイアウトは同じで、None が null ニッチになります)。

Origin システム(「ライフタイム」ではありません)

Mojo は参照の来歴を「ライフタイム」ではなくoriginで追跡します。

struct Span[mut: Bool, //, T: AnyType, origin: Origin[mut=mut]]: ...

主な型:Origin、MutOrigin、ImmOrigin、MutAnyOrigin、 ImmutAnyOrigin、MutUntrackedOrigin、ImmUntrackedOrigin、 ImmStaticOrigin。値の origin を取得するには origin_of(value) を使います。

テスト

from std.testing import assert_equal, assert_true, assert_false, assert_raises, TestSuite

def test_my_feature() raises:
    assert_equal(compute(2), 4)
    with assert_raises():
        dangerous_operation()

def main() raises:
    TestSuite.discover_tests[__functions_in_module()]().run()

mojo test CLI サブコマンドは削除されました。上記のような TestSuite.discover_tests ランナーに対して、mojo run でテストファイルを実行してください。

Dict の反復処理

Dict のエントリーは直接反復処理します。[] による逆参照は不要です。

for entry in my_dict.items():
    print(entry.key, entry.value)      # direct field access, NOT entry[].key

for key in my_dict:
    print(key, my_dict[key])

コレクションリテラル

List には可変長の位置引数コンストラクターがありません。角括弧のリテラル構文を使用してください。

# WRONG — no List[T](elem1, elem2, ...) constructor
var nums = List[Int](1, 2, 3)

# CORRECT — bracket literals
var nums = [1, 2, 3]                              # List[Int]
var nums: List[Float32] = [1.0, 2.0, 3.0]         # explicit element type
var scores = {"alice": 95, "bob": 87}              # Dict[String, Int]

List[T] はコンパイル時に負のインデックスを拒否します。lst[-1] ではなく、 lst[len(lst) - 1] を使用してください。(ライブラリー型では引き続きサポートされる場合があります。)

Variant へのアクセス

Variant[A, B] が ImplicitlyCopyable となるのは、すべてのアームが ImplicitlyCopyable の場合のみです。コピー不可のアームがある場合、Variant のインデックス指定ではコピーが発生します。型付きアームの添字構文を使用してください。

# WRONG — `values[i]` implicitly copies the Variant
var x = values[i].unwrap[T]()    # ERROR: cannot implicitly copy

# CORRECT — `values[i][T]` returns a ref to the inner value
var x = values[i][T].copy()          # or `^` to transfer

よく使うデコレーター

デコレーター用途
@fieldwise_initフィールドごとのコンストラクターを生成
@implicit暗黙的な変換を許可
@inline(.always) / @inline(.nodebug)インライン化を強制
@inline(.never)インライン化を防止
@staticmethod静的メソッド
@deprecated("msg")非推奨の警告
@doc_hiddenドキュメントに表示しない
@explicit_destroy("msg")(トレイトのみ)線形トレイト。構造体の場合は not Deinitable

コンテキストに応じたメンバー参照 — .member を優先

期待される型がすでにわかっている場合は、Type.member ではなく .member と記述してください。コンパイラーが Type.member に書き換えます。DType と AddressSpace では、コードベース全体でこの書き方が慣用的に使われています。

var v = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0)     # not SIMD[DType.float32, 4]
comptime if dtype == .bfloat16: ...                # `__eq__` arg supplies context
v.cast[.float32]()
ctx.enqueue_create_buffer[.int32](num_rows)
unsafe_stack_allocation[1, Int32, address_space=.SHARED]()

def f[dtype: DType = .float32](x: TileTensor[.bfloat16, L, MutAnyOrigin]): ...

あらゆる型の comptime エイリアスや静的メソッドで使用でき、チェーン(.red.opacity(0.5))や型付きコレクションリテラル(List[Color] = [.red])にも使えます。コンテキストは、型を宣言した var/ref、呼び出し引数の型、return の戻り先、または型付きコレクションリテラルの要素型から得られます。これらがない場合は、名前を完全修飾してください。

コンテキスト型がない場合必須の記述
型注釈のないバインディングcomptime t = DType.float32(または t: DType と注釈)
オーバーロードされた呼び出し先size_of[DType.float32]() — オーバーロードは検索されません
単独のタプルリテラルdtype in (DType.bfloat16, DType.float16)
型の位置型が期待される位置での .Foo はエラーです

数値変換 — 明示が必須

数値の変数間では暗黙的な変換はできません。明示的なコンストラクターを使用してください。

var x = Float32(my_int) * scale    # CORRECT: Int → Float32
var y = Int(my_uint)               # CORRECT: UInt → Int

リテラルは多相です — FloatLiteral と IntLiteral はコンテキストに合わせて自動的に適応します。

var a: Float32 = 0.5              # literal becomes Float32
var b = Float32(x) * 0.003921    # literal adapts — no wrapping needed
var v = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0)  # literals adapt

SIMD 操作

# Construction and lane access
var v = SIMD[.float32, 4](1.0, 2.0, 3.0, 4.0)
v[0]                              # read lane → Scalar[.float32]
v[0] = 5.0                        # write lane

# Type cast
v.cast[.uint32]()                 # element-wise → SIMD[.uint32, 4]

# Clamp (method)
v.clamp(0.0, 1.0)                 # element-wise clamp to [lower, upper]

# min/max are FREE FUNCTIONS, not methods
from std.math import min, max
min(a, b)                          # element-wise min (same-type SIMD args)
max(a, b)                          # element-wise max

# Element-wise ternary via bool SIMD
var mask = v.gt(0.0)              # SIMD[.bool, 4] — `v > 0.0` is
                                  # Scalar-only and fails to compile
mask.select(true_case, false_case) # picks per-lane

# Reductions
v.reduce_add()                     # horizontal sum → Scalar
v.reduce_max()                     # horizontal max → Scalar
v.reduce_min()                     # horizontal min → Scalar

文字列

標準ライブラリーを明示的にインポートする場合は、すべて std. プレフィックスが必要です。 削除された構文の表には、よくある修正例を示していますが、このルールは例外なく適用されます。Prelude 型(Int、String、List など)は自動的にインポートされるため、インポート文は不要です。

len(s) が返すのはバイト長であり、コードポイント数ではありません。Mojo の文字列は UTF-8 です。 バイトインデックス指定にはキーワード構文 s[byte=idx] を使います(s[idx] ではありません)。String に対する len(s) は非推奨です。s.byte_length() または s.count_codepoints() を使用してください。

split、removeprefix、removesuffix は、元の文字列を参照する StringSlice(または List[StringSlice])を返します。所有権を持つ String として実体化するには、String(...) でラップしてください。

文字列のインデックス指定(よくあるエラー)
# WRONG — compile error
var ch = s[0]
var sub = s[0:10]

# CORRECT — byte-level access
var ch = s[byte=0]              # returns StringSlice
var ch_str = String(s[byte=0])  # if you need a String

# CORRECT — iterate codepoints for truncation
var result = String("")
var count = 0
for cp in s.codepoint_slices():
    if count >= 10:
        break
    result += String(cp)
    count += 1
var s = "Hello"
len(s)                  # 5 (bytes)
s.byte_length()         # 5 (same as len)
s.count_codepoints()    # 5 (codepoint count — differs for non-ASCII)

# Iteration — `for c in s:` is deprecated; use codepoint_slices()
for cp_slice in s.codepoint_slices():
    print(cp_slice)

# Codepoint values
for cp in s.codepoints():
    print(Int(cp))      # Codepoint is a Unicode scalar value type

# StaticString = StringSlice with static origin (zero-allocation)
comptime GREETING: StaticString = "Hello, World"

# t-strings for interpolation (lazy, type-safe)
var msg = t"x={x}, y={y}"

# String.format() for runtime formatting
var s = "Hello, {}!".format("world")

エラー処理

raises には型を指定できます。try/except は Python と同様に動作します。

def might_fail() raises -> Int:          # raises Error (default)
    raise Error("something went wrong")

def parse(s: String) raises Int -> Int:  # raises specific type
    raise 42

try:
    var x = parse("bad")
except err:                               # err is Int
    print("error code:", err)

match/comptime match/if case/while case は安定化されています。パターン形式と、elif チェーンよりも優先すべき場合については、idiomatic-mojo.md §10 を参照してください。非同期処理は __async def と __await と記述します(async/await 単体でも構文として解析されますが、非同期処理は不安定であるという警告が表示されます)。サポートは未完成で、型は非公開です。現時点では非同期 Mojo を記述しないでください。

関数型とクロージャ

ラムダはありません。クロージャには、引数リストの後に {} で囲んだキャプチャリストを付けた、修飾なしの def を使用します。escaping、capturing、@__parameter、@__copy_capture は削除されました。

comptime MyFn = def(Int) -> None                  # closure trait
def runner[F: def(Int) -> None](f: F): ...        # closures are values
def apply[f: def(Int) thin -> Int](): ...         # comptime fn: must be thin

def closure(i: Int) {mut count, imm ptr, var x}:  # captures: mut/imm/var
    count += ptr[i] + x^                          # `^` at use site, not in `{}`

vectorize[simd_width](size, closure)

デフォルトは imm です。var x は所有権を持ちます。使用箇所で x^ を指定して所有権を移します。キャプチャを持つクロージャは実行時の値なので、引数として渡します(f(my_closure))。キャプチャのない thin 関数だけがコンパイル時パラメーターになれます。

型階層

AnyType
  Deinitable                      — auto __deinit__; most types
  Movable                         — __init__(out self, *, deinit move: Self)
    Copyable                      — __init__(out self, *, copy: Self)
      ImplicitlyCopyable(Copyable, take)
    RegisterPassable(Movable)
      TrivialRegisterPassable(ImplicitlyCopyable, take, Movable, RegisterPassable)

コメント