按数组大小进行类型检查?

huangapple go评论99阅读模式
英文:

Type-check by array size?

问题

我正在努力构建一个用于(有效地)排列的库;目标是允许您指定和组合n个元素上的排列。因此,例如,我想能够执行以下操作:

let p1 = Permutation([4,3,2,1])
let p2 = Permutation([1,3,2,4])
p1 * p2 // -> Permutation([4,2,3,1])

通常情况下,仅有相同大小的排列才有意义。所以,例如:

let p3 = Permutation([1,2,3])
p1 * p3 // 错误

显然,我可以在进行组合时检查大小,然后要么抛出异常要么返回nil;但这两个选项都涉及更多样板代码(要么标记每个组合都要用try,要么解包每个结果)。因为组合不同长度的排列从来都没有意义,所以我更希望在编译时出现错误 — 本质上,我希望不同长度的排列具有不同的类型。

我可以接受需要指定有限数量的类型 —— 我最终的用例可能只关心一系列有限大小。然而,最终可能会有其他类型,我可能想要与排列一起组合(例如,排列序列或语义类型化的排列子集),对于这些类型,相同的限制也会适用 —— 因此,我更希望以一种通用的方式进行类型检查。

(如果有帮助的话,本质上我正在寻找一种近似于Julia的NTuple类型的方法,这些类型是由元素的数量参数化的。)

我尝试过的

到目前为止,我最好的尝试涉及虚拟长度“类型”(即为每个长度创建一个单独的不可实例化类型):

protocol LengthProtocol { }
enum Three: LengthProtocol { }
enum Four: LengthProtocol { }

struct Permutation<Length: LengthProtocol> {
  // ...

  static func *<S: LengthProtocol>(lhs: Permutation<S>, rhs: Permutation<S>) -> Permutation<S> {
    // ...
  }
}

然而,在这里,类型推断似乎失败了:

let p1 = Permutation<Three>([1,2,3])
let p2 = Permutation<Three>([1,3,2])
p1 * p2 // 错误:无法推断泛型参数S
英文:

I'm working on building a library for (effectively) permutations; the goal is to allow you to specify and compose permutations on n elements. So, for example, I'd like to be able to do something like the following:

let p1 = Permutation([4,3,2,1])
let p2 = Permutation([1,3,2,4])
p1 * p2 // -> Permutation([4,2,3,1])

In general, it only makes sense to compose permutations of the same size. So for example:

let p3 = Permutation([1,2,3])
p1 * p3 // Error

Obviously I can check the sizes when I do the composition and either throw an exception or return nil; but both of these options involve more boilerplate (either marking every composition with try or unpacking every result). Because it never makes sense to compose permutations of different lengths, I'd prefer to have a compile-time error — in essence, I want permutations of different lengths to be of different types.

I'm ok with having to specify a finite number of types — my ultimate use case probably only cares about a finite range of sizes. However, there will eventually be other types that I might want to compose with permutations (e.g. permutation sequences or semantically-typed subsets of permutations) for which the same restriction will apply — so I'd prefer the type-checking be done in some generic way.

(If it helps, essentially what I'm looking for is a way to approximate Julia's NTuple types, which are parameterized by the number of elements.)

What I've tried

My best attempt so far involves dummy length "types" (i.e. a separate uninstantiable type for each length):

protocol LengthProtocol { }
enum Three: LengthProtocol { }
enum Four: LengthProtocol { }

struct Permutation<Length: LengthProtocol> {
  // ...

  static func *<S: LengthProtocol>(lhs: Permutation<S>, rhs: Permutation<S>) -> Permutation<S> {
    // ...
  }
}

However, type inference seems to fail, here:

let p1 = Permutation<Three>([1,2,3])
let p2 = Permutation<Three>([1,3,2])
p1 * p2 // ERROR: Cannot infer generic paramter S

答案1

得分: 3

运算符不应该是通用的。它应该使用在结构体中声明的现有的 Length 类型参数。

static func *(lhs: Permutation<Length>, rhs: Permutation<Length>) -> Permutation<Length> {
    ...
}

泛型参数 S 仅在你将运算符声明为 Permutation 之外时才需要。

此外,我不会允许公共代码使用数组来初始化 Permutation。相反,应该暴露具有特定参数数量的初始化器,并在每个初始化器上约束 Length

private init(_ array: [Int]) {
    // ...
}

init(_ a: Int, _ b: Int, _ c: Int, _ d: Int) where Length == Four {
    self.init([a, b, c, d])
}

init(_ a: Int, _ b: Int, _ c: Int) where Length == Three {
    self.init([a, b, c])
}
英文:

The operator should not be generic. It should use the existing Length type parameter, as declared in the struct.

static func *(lhs: Permutation&lt;Length&gt;, rhs: Permutation&lt;Length&gt;) -&gt; Permutation&lt;Length&gt; {
    ...
}

The generic parameter S is only required, if you declare the operator outside of Permutation.

Also, I'd not allow public code to initialise Permutation with an array. Instead, expose initialisers with specific numbers of parameters, and constrain Length on each initialiser.

private init(_ array: [Int]) {
    // ...
}

init(_ a: Int, _ b: Int, _ c: Int, _ d: Int) where Length == Four {
    self.init([a, b, c, d])
}

init(_ a: Int, _ b: Int, _ c: Int) where Length == Three {
    self.init([a, b, c])
}

huangapple
  • 本文由 发表于 2023年7月27日 19:59:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76779519.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定