Сегодня я снова поигрался со Swift и нуждался в функции undefined()
. В основном это функция, которая может быть любого типа, но вылетает, когда она действительно запускается / оценивается. Это полезно, если у вас еще не было времени на реализацию определенного выражения, но вы хотите проверить, проверяет ли тип программы.
Я также реализовал Result<T>
и (сомнительную) функцию chain
, которая возвращает левую Result<>
в случае неудачи, в противном случае - правую Result<>
. Следовательно, подпись chain<L,R>(l : Result<L>, r : @autoclosure () -> Result<R>) -> Result<R>
. Я определил правое Result<>
как @autoclosure
, потому что нет необходимости оценивать его, если и только если левый является неудачным.
Меня не интересует полезность (или улучшения) чего-либо из этого, меня просто интересует, почему моя программа вылетает в строке, отмеченной [L3]
.
Обратите внимание, что
- как и ожидалось
[L1]
работает нормально (||
лениво оценивается) [L2]
работает нормально (chain
тоже ленив в правом аргументе)
Но как ни странно
[L3]
вызывает сбой программы при оценкеundefined()
Насколько я понимаю, L2 и L3 должны быть эквивалентны во время выполнения: L3 просто сообщает средству проверки типов то, что он уже знает ... Тот же эффект, кстати, произойдет, если вы измените тип undefined
на () -> Result<T>
(вместо () -> T
), тогда он все работает даже без as Result<String>
.
Это весь мой код:
import Foundation
enum Result<T> {
case Success(@autoclosure () -> T);
case Failure(@autoclosure () -> NSError)
}
func undefined<T>(file:StaticString=__FILE__, line:UWord=__LINE__) -> T {
fatalError("undefined", file:file, line:line)
}
func chain<L,R>(l : Result<L>, r : @autoclosure () -> Result<R>) -> Result<R> {
switch(l) {
case .Failure(let f):
return .Failure(f())
case .Success(let _):
return r()
}
}
func testEvaluation() {
let error = NSError(domain: NSPOSIXErrorDomain, code: Int(ENOENT), userInfo: nil)
let failure : Result<String> = .Failure(error)
assert(true || undefined() as Bool) // [L1]: works
let x : Result<String> = chain(failure, undefined() as Result<String>) // [L2]: works
print("x = \(x)\n")
let y : Result<String> = chain(failure, undefined()) // [L3]: CRASHES THE PROGRAM EVALUATING undefined()
print("y = \(y)\n")
}
testEvaluation()
Вы не поверите, но результат работы программы:
x = (Enum Value)
fatal error: undefined: file main.swift, line 27
Illegal instruction: 4
Этого не может быть! Почему as Result<String>
(это единственное различие между L2 и L3) изменит вывод программы? Это должно полностью обрабатываться в средстве проверки типов, а это означает, что во время компиляции, нет? Ошибка компилятора?
Самый быстрый способ воспроизвести это:
- скопируйте весь мой код в буфер обмена
cd /tmp
pbpaste > main.swift
xcrun -sdk macosx swiftc main.swift
./main
фактический выход:
x = (Enum Value)
fatal error: undefined: file main.swift, line 27
Illegal instruction: 4
ожидаемый результат:
x = (Enum Value)
y = (Enum Value)
println(file)
прямо передfatalError
, он напечатает ‹EXPR› без сбоев. - person qwerty_so   schedule 17.01.2015print("--> \(file):\(line)\n")
и теперь вылетает какx = (Enum Value)\n --> main.swift:28\n fatal error: undefined: file main.swift, line 28
- person Johannes Weiss   schedule 18.01.2015T
изundefined()
как() -> Result<String>
, а не какResult<String>
, а затем вообще не помещает его в закрытие. - Кстати, сchain(failure, { undefined() }() )
работает как положено - person Martin R   schedule 18.01.2015() -> Result<String>
(что является закрытием), он все равно не должен его запускать. И да, я тоже пробовал `chain (failure, {undefined ()} ()), и она работает ... - person Johannes Weiss   schedule 18.01.2015undefined()
(которое является значением заполнителя типа<T>
) выводится как() -> Result<String>
. Так что по какой-то причинеundefined()
не завернут в автозакрытие. Компилятор только генерирует вызов дляundefined()
и передает возвращаемое значение (которое является() -> Result<String>
функцииchain()
. Если вы установите точку останова вundefined()
, то вы увидите, чтоT
это что-то вроде(Builtin.RawPointer) () -> Module.Result<String>
. - person Martin R   schedule 18.01.2015undefined
вообще компилируется без атрибутаnoreturn
: developer.apple.com/library/prerelease/ios/documentation/Swift/. - person Joseph Lord   schedule 18.01.2015noreturn
находится вfatalError
, вот почему. Но нормальное определениеundefined
в любом случаеundefined = undefined
, поэтому исходная реализация (и единственная допустимая безnoreturn
) былаfunc undefined<T>(file:StaticString=__FILE__, line:UWord=__LINE__) -> T { fatalError("undefined", file:file, line:line); return undefined() }
. Но тогда, поскольку у нас естьnoreturn
,return
не нужен. - person Johannes Weiss   schedule 18.01.2015fatalError
и сделалfunc undefined<T>(file:StaticString=__FILE__, line:UWord=__LINE__) -> T { return undefined() }
. Поведение такое же, за исключением того, что происходит сбой при переполнении стека, а не при утверждении. Но все же: он оценивает выражение, когда его не должно быть. - person Johannes Weiss   schedule 18.01.2015