Skip to content

1.0 への移行

Onion は、まだ影響が小さいうちに、1.0 の前に構文と API を整理しています。 互換性を壊す変更を、コードの直し方と一緒にここにまとめます。

1.0 以降は互換性を約束します。1.x でコンパイルできるプログラムは、 その後の 1.x でもコンパイルでき、意味も変わりません。

ret は yield になりました

do ブロックの最後の値は yield で書きます。

// 以前
val sum = do[Option] { a <- getA(); b <- getB(); ret a + b }

// 現在
val sum = do[Option] { a <- getA(); b <- getB(); yield a + b }
  • 変わったこと: yield は予約語になりました。ret は普通の識別子に戻りました。
  • 表示されるもの: 古いコードでは「ローカル変数 ret が見つかりません」と表示され、yield を使うようヒントが出ます。
  • yield という名前の Java メソッド: バッククォートで呼び出します(例: Thread::`yield`())。
  • プロジェクト全体を書き換える:
    grep -rl '\bret\b' --include='*.on' . | xargs perl -pi -e 's/\bret\b/yield/g'
    
    ret を変数名として使っている場合は、結果を確認してください。

2147483648 の直前には - が必要になりました

リテラル 2147483648(と 9223372036854775808L)は、-2147483648 のように単項 - のオペランドそのものである場合だけ有効です。 それ以外の場所では、以前は黙って最小値に化けていました。

// 以前: コンパイルでき、意外な値になっていた
val a = 1 - 2147483648          // -2147483647(ラップアラウンドによる)
val b = -2147483648 as Long     // 2147483648: `as` は `-` より強く結合する
val c = -2147483648 / 2         // 1073741824: `/` は `-` より強く結合する

// 現在
val a = 1 - 2147483648L         // または 1L - 2147483648L
val b = -2147483648L            // または (-2147483648) as Long
val c = (-2147483648) / 2       // -1073741824
  • 変わったこと: 最小値の絶対値は、トークンではなく、それを符号反転する場所で検査します。
  • 表示されるもの: リテラルの位置に「整数リテラルがIntの範囲(-2147483648..2147483647)を超えています -- Long型リテラルにするには末尾にLを付けてください。」と表示されます(L 付きなら Long の範囲のメッセージです)。
  • 引き続き使えるもの: -2147483648、x + -2147483648、3 * -2147483648、-9223372036854775808L、0x80000000 のような 16 進数・2 進数のビットパターン。

10進数の B/S リテラルは符号付きの範囲に収める必要があります

10進数の Byte リテラルは -128..127、10進数の Short リテラルは -32768..32767 の範囲になりました。 Java と同じです。以前は、範囲外の値が黙って桁あふれしていました。

// 以前: コンパイルが通り、意外な値になる
val mask: Byte = 255B           // -1
val port: Short = 40000S        // -25536
val n = -128B as Int            // 128: `as` は `-` より強く結合する

// 現在
val mask: Byte = 0b11111111B    // -1。ビットパターンとして書く
val port: Int = 40000           // Short には 40000 は入らない
val n = (-128B) as Int          // -128
  • 変更点: 2147483648 と同じく、128B と 32768S は - のオペランドそのものである場合だけ有効です。
  • 表示されるエラー: リテラルの位置に「整数リテラルがByteの範囲(-128..127、またはビットパターンとして0b11111111まで)を超えています -- ...」が出ます(S サフィックスなら Short)。
  • 引き続き使えるもの: 0b11111111B のような2進数のビットパターンと、0xFFFFS のように S を付けた16進数。
  • 16進数と B: 16進数リテラルの B は数字であり、接尾辞ではありません。0xAB は Int の 171、0xFFB は Int の 4091 です。Byte にしたいときは 0b11111111B か -1B と書いてください。
  • そのようなリテラルが Byte に収まらなくなった箇所(val x: Byte = 0xFFB、Byte 引数への f(0xFFB))では、エラーにその理由を示すヒントが付きます。
  • まだ収まる箇所では何も報告されず、値だけが変わります。bs[0] = 0x1B は 27 を格納し(以前は 1)、val y = 0x7FB は 2043 です(以前は Byte の 127)。末尾が B の16進数リテラル(0x...B)をコードから検索してください。

config ファイルの引用符付き String は、引用符を外して読むようになりました

shape name = config のレコードは、二重引用符で囲まれた String の値を、引用された文字列として読みます。 これで、前後の空白を持つ値も print と parse を往復できます。

# 以前: 引用符も値の一部だった
name = "Alice"          # "Alice" と読む(7 文字)

# 現在
name = "Alice"          # Alice と読む(5 文字)
name = "  Alice  "      #   Alice   と読む(空白を保つ)
name =   Alice          # Alice と読む(引用符のない値はこれまでどおり trim)
name = "\"Alice\""      # "Alice" と読む(引用符を文字として残す書き方)
  • 変わったこと: 引用符の中では、\" が引用符、\\ がバックスラッシュです。 それ以外のバックスラッシュはそのまま残るので、"C:\temp" は C:\temp と読みます。
  • 表示されるもの: 引用符を文字として使っていたファイルもそのまま parse に成功します。 そのため、エラーもヒントも出ません。値の外側の引用符が消えるだけです。 設定ファイルの中から " で始まる値を探してください。 閉じる " のない値や、中に裸の " がある値("a" and "b")は、行番号付きの Defect になります。
  • 対象は String 成分だけです: Int や Boolean などのスカラーは、これまでどおりに読みます。

空の [] / [:] は、隣の要素の型になりました

リストリテラル、またはマップリテラルの値に空の [] / [:] があると、他の要素が揃っている型として扱います。 以前は、リテラル全体が Object に広がっていました。

val adj = [[1, 2], []]

// 以前: List[Object] なので、adj[1].add(5) は E0005 で、次の行はコンパイルできた
adj.add("header")

// 現在: List[List[Int]] なので、adj[1].add(5) はコンパイルでき、次の行は E0005
adj.add("header")

// 以前の型を保つには、型を注釈する
val xs: List[Object] = [[1, 2], []]
  • 変わったこと: 空のリテラルは、隣の要素の後に、その型で型付けされます。要素の評価順は、これまでどおりソースの順です。
  • 表示されるもの: 異なる種類の要素を混ぜたコードや、List[Object] の引数に渡したコードで、List[List[Int]].add(String)(または Map[String, List[Int]].set(...))を示す E0005 が出ます。
  • そのまま動くもの: 明示した型注釈が優先されます。型の分かる隣の要素がないリテラル([[], []])は、これまでどおりです。