English

トラブルシューティング & よくある間違い

LuneScript の学習中に遭遇しやすいエラーとその解決方法をまとめました。
他の言語(特に Lua, Go, Rust など)の経験者がハマりやすいポイントを中心に解説します。

文字リテラルの構文

間違い: シングルクォートを使ってしまう

// @lnsFront: skip

let c = 'a';    // Error
let c = ?'a';   // Error
                

正解: ? の直後に文字を書く

// @lnsFront: skip

let c = ?a;     // 文字コード (int) になる
let sp = ? ;    // スペースの場合も同様
let zero = ?0;  // 数字の文字コード
                

LuneScript では、文字リテラルは整数値(文字コード)として扱われます。

ミュータブルなメソッド定義

間違い: mut を前置してしまう

// @lnsFront: skip

class MyClass {
    let mut val: int;
    // Error: illegal field or method declaration
    pub mut fn inc() {
        self.val = self.val + 1;
    }
}
                

正解: mut は引数リストの後ろに置く

// @lnsFront: skip

class MyClass {
    let mut val: int;
    // メソッドが自身 (self) を書き換えることを示す
    pub fn inc() mut {
        self.val = self.val + 1;
    }
}
                

メソッド定義において、mut は「このメソッドは self を変更する」という副作用の宣言として機能するため、シグネチャの一部(後ろ側)に記述します。
なお、戻り値がある場合は pun fn func() mut : int のように戻り値の型の前になります。

ADT のパターンマッチ

間違い: if let を使おうとする (Rust風)

// @lnsFront: skip

// Error: illegal exp
if let .Val( v ) = val {
    print( v );
}
                

正解: match 文を使用する

// @lnsFront: skip

match val {
    case .Val( v ) {
        print( v );
    }
    default {
        // マッチしなかった場合の処理
    }
}
                

LuneScript の現行バージョンでは、代数的データ型 (Algebric Data Type) の分解には match 文を使用するのが基本です。

Nilable 型の操作と unwrap

間違い: Nilable 型をそのまま非 Nilable として扱う

// @lnsFront: skip

let val: int! = 10;
// Error: type mismatch int! (nilable) to int
let num: int = val; 
                

正解: unwrap を使って安全に取り出す

// @lnsFront: skip

let val: int! = 10;
// nil だった場合のデフォルト値を指定する
let num: int = unwrap val default 0;

// または if let で絞り込む
if let v = val {
    print( v ); // ここでは v は int 型
}
                

unwrap は「値が nil でないことを保証する」か「nil の場合の代替値を提供する」ために使われます。

マクロの構文エラー

間違い: 展開演算子 ,, を忘れる

// @lnsFront: skip
macro _dprint( val: __exp ) {
    print( val ); // Error: val は __exp 型であり、そのままでは使えない
}
                

正解: ,, を使って式を展開する

// @lnsFront: skip
macro _dprint( val: __exp ) {
    print( ,,val ); // OK: 式が展開される
}
                

マクロ引数(__exp, sym, __block など)をコード内で利用するには、必ず ,, で展開する必要があります。

間違い: Lua の予約語をシンボル名に使ってしまう

// @lnsFront: skip
macro _measure( block: __block ) {
    {}
    {
        let start = os.clock();
        ,,block
        let end = os.clock(); // Error: 'end' は Lua の予約語
        print( end - start );
    }
}
                

正解: 予約語を避けた名前にする

LuneScript は Lua に変換されるため、マクロで展開されるコード内の変数名が Lua の予約語(end, function, repeat など)と衝突するとエラーになります。endTime などの別の名前に変更してください。

間違い: __block 引数に直接ステートメントを書く

// @lnsFront: skip
_measure( print("hello") ); // Error: 式ではなくブロック {} が必要
                

正解: {} で囲って渡す

// @lnsFront: skip
_measure( { print("hello"); } );
                

また、LuneScript の最新の文法では __block の代わりに ## を使った引数省略機能の利用が推奨される場合があります。コンパイラの警告に従って調整してください。

間違い: macro-statement 内で通常の関数を定義・呼び出ししようとする

// @lnsFront: skip
macro _Test() {
  {
    fn createFunc( name: str ): stat { // Error
       return `{ fn ,,,name() {} };
    }
  }
  ,,createFunc("hoge");
}
                

正解: macro-statement は制限された環境であることを理解する

macro-statement ブロック内では、型システムやユーザー定義関数が完全に利用できるわけではありません。基本的には、組込み型と組込み関数を用いた計算、および `{} によるコード生成に専念させるのが安全です。

間違い: ,,, 演算子の範囲が不明確

// @lnsFront: skip
stats.insert( `{
    let ,,, "v_%s"(name) = ,,i; // Error: どこまでが名前生成式か不明
} );
                

正解: ~~ で終端を明示する

// @lnsFront: skip
stats.insert( `{
    let ,,, "v_%s"(name) ~~ = ,,i; // OK: ~~ で式の終わりを教える
} );
                

シンボルを動的に生成する ,,, 演算子は、その後に続く要素と結合して解釈されるのを防ぐため、~~ による区切りが必要になるケースが多々あります。