В Swift, можете ли вы создать протокол, который требует определенной функции, только когда выполняются определенные условия для связанных типов? - PullRequest
0 голосов
/ 25 марта 2019

Я хотел бы выразить протокол Swift, похожий на следующие два фрагмента, которые оба не компилируются.

Попытка 1:

protocol AbstractFunction {
    associatedtype Domain
    associatedtype Codomain

    func apply(_ x: Domain) -> Codomain

    static var identity: Self where Domain == Codomain { get }
}

Попытка 2:

protocol AbstractFunction {
    associatedtype Domain
    associatedtype Codomain

    func apply(_ x: Domain) -> Codomain

    static func identity() -> Self where Domain == Codomain { get }
}

Первое даже недействительно в грамматике Свифта, а второе не работает с 'where' clause cannot be attached to a non-generic declaration.

Оба эти примера пытаются выразить протокол, который описывает функции, которые не являются экземплярами фактического типа функции (A) -> B. Если у вас есть типы Vector2 и Vector3, можно представить создание типов Matrix2x2, Matrix2x3 и Matrix3x3 и их соответствие протоколу AbstractFunction. Домен MatrixNxM будет VectorM, а кодомен будет VectorN. Квадратные матрицы имеют тождественную матрицу, но концепция тождественной матрицы (или действительно тождественной функции) не имеет смысла, когда домен и кодомен различаются.

Следовательно, я бы хотел, чтобы протокол AbstractFunction требовал соответствия типов для обеспечения идентичности, но только в случае, когда Domain == Codomain. Это возможно?

Ответы [ 2 ]

1 голос
/ 25 марта 2019

Вы можете достичь этого, объявив второй более строгий протокол:

protocol AbstractFunction {
    associatedtype Domain
    associatedtype Codomain

    func apply(_ x: Domain) -> Codomain
}

protocol AbstractEndofunction: AbstractFunction where Codomain == Domain {
    static var identity: Self { get }
}

Пример функции Int -> Int:

final class IntFunction: AbstractEndofunction {
    typealias Domain = Int

    static var identity = IntFunction { $0 }

    private let function: (Int) -> Int

    init(_ function: @escaping (Int) -> Int) {
        self.function = function
    }

    func apply(_ x: Int) -> Int {
        return function(x)
    }
}
0 голосов
/ 25 марта 2019

Я не думаю, что вы можете сделать это. Тем не менее, я вижу два других способа, которые могут вам помочь.

Используя необязательный тип для identity, вы указываете, что определенный тип, который реализует AbstractFunction, может иметь или не иметь идентичность. Например:

final class ConcreteFunctionWithoutIdentity: AbstractFunction {
  typealias Domain = Int
  typealias Codomain = Int

  func apply(_ x: Int) -> Int {
    return 0
  }

  static var identity: ConcreteFunctionWithoutIdentity?
}

// Using
if let identity = ConcreteFunctionWithoutIdentity.identity else {
   // It will not fall here, since ConcreteFunctionWithoutIdentity doesn't have identity
   ...
}

final class ConcreteFunctionWithIdentity: AbstractFunction {
  typealias Domain = Int
  typealias Codomain = Int

  func apply(_ x: Int) -> Int {
    return 0
  }

  static var identity: ConcreteFunctionWithtIdentity? {
    // return something
  }
}

if let identity = ConcreteFunctionWithtIdentity.identity else {
   // It will fall here, since ConcreteFunctionWithIdentity indeed have identity
   ...
}
...