#swift #generics #ambiguous
#swift #универсальные #неоднозначный
Вопрос:
Я пишу класс синтаксического анализатора, который ожидает считывания последовательности токенов в определенном порядке. Некоторые продукты в его грамматике имеют необязательные нетерминалы, поэтому я хотел создать универсальную функцию «может быть», которой можно было бы передать функцию, ответственную за синтаксический анализ нетерминала в качестве обратного вызова. Обычно функция выдает ошибку при сбое, но, поскольку в некоторых случаях это необязательно, функция maybe подавляет ошибку. Однако Swift выдает ошибку «Тип выражения неоднозначен без дополнительного контекста», и я не могу определить правильный набор приведений и / или типов, чтобы устранить его неоднозначность.
Вот минимальный объем кода, который я смог написать, чтобы воссоздать ошибку:
public struct VariableDeclaration {
public let identifier: Identifier
public let type: String?
}
public struct Identifier { }
public class Parser {
public func parseVariableDeclaration() throws -> VariableDeclaration {
let identifier = try self.parseIdentifier()
let type = self.maybe(self.parseType)
return VariableDeclaration(identifier: identifier, type: type)
}
public func parseIdentifier() throws -> Identifier { return Identifier() }
public func parseType() throws -> String { return "" }
public func maybe<T>(_ callback: (Parser) -> () throws -> T) -> T? {
do {
return try callback(self)()
}
catch {
return nil
}
}
}
И вот некоторые из моих неудачных попыток устранения неоднозначности проблемной строки:
let type: String? self.maybe(self.parseType)
let type = self.maybe(self.parseType) as String?
let type = self.maybe<String>(self.parseType)
Ответ №1:
Проблема здесь не в общих параметрах. Ваша первая и вторая попытка укажут компилятору, какой тип T
должен быть.
Проблема заключается в значении, которое вы передаете как callback
, которое имеет следующую подпись:
(Parser) -> () throws -> T
Вы передаете, в self.parseType
котором есть следующая подпись:
() throws -> String
Что будет работать, так это использование Self.parseType
(обратите внимание на заглавную S
букву) или Parser.parseType
в качестве значения для callback
.
В качестве альтернативы, вы могли бы определить maybe
так:
public func maybe<T>(_ callback: (Parser) throws -> T) -> T? {
do {
return try callback(self)
} catch {
return nil
}
}
А затем вызовите его следующим образом:
let type = self.maybe { try $0.parseType() }
Комментарии:
1. Я не смог приступить
Self.parseType
к компиляции (Self
Parser
по какой-либо причине он отказался преобразовываться в), ноParser.parseType
работал успешно. Предложение lambda также сработало. Спасибо за подробный ответ.2. @Jared Ах, это потому
Parser
, что это (не окончательный) класс. ТакимSelf
образом, в подклассе не будет (точного)Parser
…
Ответ №2:
Я не уверен, действительно ли это то, что вы хотите сделать, но если вы хотите сохранить подпись maybe
, вам нужно передать частичную ссылку на метод следующим образом:
let type = self.maybe(Parser.parseType)