Swift REPL で ハマった事例
以下のコードを、空行を区切りにして ブロックごとに Swift REPLにコピペしたとき、文法的には 間違いがないはずが、コンパイルエラーとなった。
infix operator &&= : AssignmentPrecedence
func &&= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs && rhs() }
infix operator ||= : AssignmentPrecedence
func ||= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs || rhs() }
var a = true
let b = false
a ||= b
print(a)
a &&= b
print(a)
swift replの実行ログを見る場合、
行番号の後ろの>が入力の始まり行を示し、その後に続く.の行が一度に入力された範囲を示す。
下の例だと、入力の単位は、行1〜4、行5〜6、行7〜8 となる。
行7でエラーのため、行7〜8は無効となり、その次の入力が再び 行7〜となる。
~ % swift repl
Welcome to Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1).
Type :help for assistance.
1> infix operator &&= : AssignmentPrecedence
2. func &&= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs && rhs() }
3. infix operator ||= : AssignmentPrecedence
4. func ||= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs || rhs() }
5> var a = true
6. let b = false
a: Bool = true
b: Bool = false
7> a ||= b
8. print(a)
˄
╰─ error: operator is not a known binary operator
7> a &&= b
8. print(a)
˄
╰─ error: operator is not a known binary operator
7>
error: operator is not a known binary operator
カスタム演算子が認識されていませんね。
Geminiに聞いたところ、次のことを教えてくれました。
上の(実行ログの)コードは、何故エラーですか?
そのコードがエラーになる主な理由は、Swift REPL(Read-Eval-Print Loop)が複数行にまたがるカスタム演算子の定義を正しく処理できないためです。
REPLは、入力されたコードを1行ずつ評価して実行するように設計されています。しかし、カスタム演算子の定義は、通常、複数の独立したステップ(infix operatorの宣言とfuncによる実装)で構成されます。REPLでコードを一行ずつ実行していくと、カスタム演算子を使用するa||=bの行が実行される時点で、コンパイラは||=演算子をまだ認識していない可能性があります。
infix operator宣言とfunc実装の行が分かれてることが原因のように読み取れたので、次のように 1行で書いてみたが、結果は同じであった。
~ % swift repl
Welcome to Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1).
Type :help for assistance.
1> infix operator &&= : AssignmentPrecedence; func &&= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs && rhs() }
2. infix operator ||= : AssignmentPrecedence; func ||= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs || rhs() }
3> var a = true
4. let b = false
a: Bool = true
b: Bool = false
5> a ||= b
6. print(a)
˄
╰─ error: operator is not a known binary operator
5>
その後の試行錯誤の結果、コピペして実行する単位の問題であることが分かった。
つまり、カスタム演算子定義と、その演算子の利用は、一度にコピペして実行する必要がある。ということです。
初めて知りました。
冒頭のコードをすべてコピペして 一度で実行すると、エラーになりませんでした!
~ % swift repl
Welcome to Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1).
Type :help for assistance.
1> infix operator &&= : AssignmentPrecedence
2. func &&= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs && rhs() }
3. infix operator ||= : AssignmentPrecedence
4. func ||= (lhs: inout Bool, rhs: @autoclosure () -> Bool) { lhs = lhs || rhs() }
5.
6. var a = true
7. let b = false
8.
9. a ||= b
10. print(a)
11.
12. a &&= b
13. print(a)
true
false
a: Bool = false
b: Bool = false
14>
REPL環境で実行する場合、複数行のコードをまとめてコピペしたとき、そのコピペしたブロックのどこかに エラーがあると、そのブロック全体が無効となるため、これまで、一定のかたまり単位でコピペして コードを確定させてく(癖)使い方をしていました。
カスタム演算子の定義がある場合は、そのやり方が「裏目に出た」ということです。
ちょっとしたswiftコードの実行はREPL環境で済ますことが多いので、(良し悪しは別として)原因が分かってスッキリしました。
数年前にも、正しい文法が弾かれた記憶があります。その時は 深く追及しませんでしたが、今は生成AIが簡単に使えるので、大変便利な時代となりました。
以上