【问题标题】:Is it possible in Swift to require an associated type to conform to an associated protocol of an associated type?在 Swift 中是否可以要求关联类型符合关联类型的关联协议?
【发布时间】:2019-03-23 05:46:07
【问题描述】:

我正在尝试(基本上没有理由)制定一个描述类别理论的类别的协议。我试图想出这样的东西。

protocol Category {
    associatedtype Object: Protocol
}
protocol Hom {
    associatedtype C: Category
    associatedtype Source: C.Object
    associatedtype Target: C.Object
}

特别是,我希望每个 Hom 类型都有一个关联的类别 C 以及一个关联的 Source 和 Target 类型,它们都是该类别中的对象。因此,我为每个 Category 关联了一个 Object 协议,并试图使 Hom 的 Source 和 Target 符合相应 Category 的 Object 协议。上面的代码编译失败

Type 'Self.Source' constrained to non-protocol, non-class type 'Self.C.Object'

这个错误至少不清楚,因为 C.Object 被声明为协议。有什么办法可以解决这个问题吗?

编辑:

正如 Rob 所指出的,按原样编写的代码没有多大意义。 Protocol 是 ObjC 的一个特殊类,不是描述协议的类型。此外,没有描述所有协议的类型,因为协议本身不能符合协议,因为它们只是对其他类型的要求。我正在寻找的是 Any.Protocol、Sequence.Protocol 等都是其实例的元类型。

我将更详细地说明我要描述的构造类型。

一个类别是一个对象类型和一个对象的每对实例之间的同态类型。对于Object的两个实例,A和B,同态的类型一般写成Hom(A,B),但我会写Hom<A,B>更快捷。然后类别配备具有签名<A: Object, B: Object, C: Object>(_ f: Hom<A,B>, _ g: Hom<B,C>) -> Hom<A,C> 的组合。

如果 f 是 Hom<A,B> 的一个实例,则 A 称为 f 的源或域,B 称为 f 的目标或共域。

类型本身就是一个类别,其中 Object 是所有类型的元类型,Hom<A,B> = (A) -> B

在 Swift 中分类很难的主要原因是 Swift 没有依赖类型。无法描述对象类型为 Int 的类别,因为无法拥有类型 Hom<0,0>。但是,如果要求 Object 类型是元类型,那么突然间Hom<A,B> 对类型系统进行描述是一件有意义的事情,因为元类型的实例是一种类型(我认为),它可以是一个泛型参数。这就是我试图通过设置 Object: Protocol 来描述的内容。

在 Swift 中,我真的很想描述

protocol Category {
    associatedtype Object: Metatype
    associatedtype Hom<A: Object, B: Object>
    func compose<A: Object, B: Object, C: Object>(_ f: Hom<A,B>, then g: Hom<B,C>) -> Hom<A,C>
}

但这也不是首发,因为关联类型不能有泛型参数。

在我的用例中,我有一个描述有限生成的阿贝尔群的协议和一个描述有限生成的单位环的协议,我很想编写通用代码,它不关心它是否与 GroupHom&lt;A,B&gt; where A: AbelianGroup, B: Abelian Group 一起使用,RingHom&lt;A,B&gt; where A: Ring, B: Ring,或(A) -&gt; B,因为它们都配备了正确的构图。

这可能是不可能的,我愿意接受。请让我知道这是否足够不同,应该作为一个单独的问题提出。

【问题讨论】:

  • associatedtype Object: Protocol 几乎可以肯定没有做你认为它正在做的事情。 Protocol 不是 Swift 中的类型,在这种情况下,它并不意味着“Swift 协议”。它是您从 ObjC 运行时(可能通过 Foundation)导入的 ObjC 类,其中包含有关 ObjC 协议的元数据。 Swift 等价物是 Any.Protocol,但它本身不是协议或类,所以你不能将 Object 约束到它。
  • (而且Any.Protocol 甚至没有真正捕捉到你想说的话,因为其他协议不是Any.Protocol 的子类型)

标签: swift swift-protocols associated-types


【解决方案1】:
associatedtype Object: Protocol

这条线并不意味着你认为它的意思。 Protocol 不是 Swift 的一部分。它是 ObjC 运行时的一部分。 (这是一个非常令人困惑的导入。)

但即使它确实是那个意思,我也不相信它会对你有所帮助。重要的是要认识到,在 Swift 中,协议不符合协议,具有关联类型的协议不是类型。它们是对类型的约束。这往往会蔓延到各处,让人们大吃一惊。

我的怀疑是您正在寻找沿着这些路线建模事物(请注意,我的范畴理论非常薄弱,所以如果我在点上使用了错误的术语,请原谅我)。

我们想要达到一个可以说“一个类别具有对象集合和这些对象之间的箭头集合”的程度。为了实现这一目标,我认为我们希望从通用 Arrow 开始:

protocol Arrow {
    associatedtype Source
    associatedtype Target
    var apply: (Source) -> Target { get }
}

同态是映射回其自身类型的箭头。

protocol Homomorphism: Arrow where Source == Target {
    typealias Object = Source
}

这样,我们可以表达一个类别:

protocol Category {
    associatedtype Object
    associatedtype Arrow: Homomorphism where Arrow.Object == Object
}

我想谈谈整数和函数的类别(我相信这是一个合适的类别)。所以首先我需要函数。

struct Function<Source, Target>: Arrow {
    let apply: (Source) -> Target
}

extension Function: Homomorphism where Source == Target {}

然后我可以声明类别。

struct Integers: Category {
    typealias Object = Int
    typealias Arrow = Function<Int, Int>
}

并创建一个态射。

let increment = Function(apply: { (x: Int) in x + 1 })

我认为这在某种程度上是您正在寻找的方向。

【讨论】:

  • 这与我要找的很接近。特别是,您对协议的含义以及协议如何不符合协议的解释非常有用。同态不需要具有相同的源和目标。自同态是具有相同源和目标的同态,因此您帮助我实现了自同态,无论如何它们都是一些最重要的同态。您描述的是类型类别中 Int 的自同态。我已经编辑了我的问题,以便更清楚地了解我想要描述的那种结构。非常感谢您的帮助!
【解决方案2】:

dg

在swift中你不能使用协议关联类型作为关联类型,因为它没有定义类型是什么。

Associated Type 只能作为类型的约束,像这样:

protocol Category {
    associatedtype Object1:Equatable
}

class Homs:Category{
    typealias Object1 = Int

    func sum(element:Object1){

        print(element+element)
    }
}

或者像这样

protocol Category {
    associatedtype Object1: Equatable
}

protocol Homs {
    associatedtype Cat:Category
    associatedtype Source: Category where Source.Object1 == Cat.Object1
    associatedtype Target: Category where Target.Object1 == Cat.Object1
}

您的代码无法编译的原因是,您的关联类型目标被限制为实现协议的关联类型(未定义)。有人需要先定义它才能用作约束。

解决问题的方法可能是生成一个泛型类。来看看吧:

protocol Category {
    associatedtype Object1: Equatable
}

class Homs<Cat:Category,Source,Target> where Cat.Object1 == Source && Cat.Object1 == Target.Object1{

}

另一种方法是创建一个通用的 Category 类,以及 Category 类、category Class、Source 和 Target 的类型符合 Type 的协议:

class Category<T>{
}

protocol Homs {
    associatedtype ObjectType
    associatedtype Cat:Category<ObjectType>
    associatedtype Source where Source == ObjectType
    associatedtype Target where Target == ObjectType
}

类似于第二个例子:

protocol Category {
    associatedtype Object1: Equatable
}

protocol Homs {
    associatedtype Cat:Category
    associatedtype Source: Category where Source.Object1 == Cat.Object1
    associatedtype Target: Category where Target.Object1 == Cat.Object1
}

请记住,您不能将具有关联类型的协议用作关联类型的类型约束或变量或常量的类型,首先需要有人定义关联类型。

希望对你有所帮助。

【讨论】:

  • 这是信息丰富的,但不是我想要的。为了清楚起见,我编辑了这个问题。我不是在寻找 Source 和 Target 等于类别中的 Object 类型。相反,我希望 Source 和 Target 是元类型 Object 的子类型。我不确定这是否可能。
  • 如果你的提议是调用不同类型的同一个“函数”,(在这种情况下是组合函数),只需要制作一个具有所有功能的基础协议,制作其他协议(阿贝尔群和环) 在相关类中实现这些协议或扩展类型,如 Int、Double 或任何你想要的。对于最终用途,例如,您可以创建一个像这样的变量 -> var baseClass:BaseProtocol? 例如使用实现 BaseProtocol 的类来设置它,例如从 BaseProtocol 扩展的 Int:var baseClass;BaseProtocol? = Int()baseClass.compose(a:5,b:3)
  • @deaton.dg 那么你需要像这样转换结果:var result:Category&lt;Int,Int&gt; = baseClass.compose(a:5,b:3) as! Category&lt;Int,Int&gt; .
  • 我不确定我是否理解您的意思。当我知道组合定义明确时,您是否建议我提供一个类型擦除的组合并依赖可选的展开?那肯定行得通,但我宁愿在这方面有编译时安全性。为了清楚起见,我已经将我希望描述的多态性的简化代码示例与这些协议联系起来。 pastebin.com/x4kg95Qs
猜你喜欢
  • 1970-01-01
  • 2018-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多