Как мне устранить неоднозначность этого вызова универсальной функции с параметром функции в Swift?

#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)