对于1:1类型/类型-类-实例关系,是否有可行且类型安全的替代方案?
这个问题最初是在我研究动态 JS 类型验证器时出现的,它依赖于字典传递样式作为一种相当简单的类型类机制,但我认为它也适用于 Haskell 或其他具有类型类机制的语言。
起初我认为在具有字典传递样式的设置中允许每个类型有多个类型类实例不会有太大问题,因为我可以完全控制类型类解析。但实际问题似乎是维护类型安全而不是类型类解析,因为我将使用类型验证器进行演示。由于代码是类型注释的,因此在某种程度上与语言无关。
// please assume `Number` to be a non-polymorphic `Int` type
Sum.Monoid // Monoid<Number>
Prod.Monoid // Monoid<Number>
Function.Monoid // Monoid<b> -> Monoid<(a -> b)>
Function.Monoid(Prod.Monoid) // Monoid<(a -> Number)>
Function.Monoid(Prod.Monoid).append // (a -> Number) -> (a -> Number) -> a -> Number
当您应用这些类型时,类型安全会受到影响,因为可以在没有验证器抱怨的情况下编写以下精心设计的表达式:
Function.Monoid(Prod.Monoid)
.append(inc) (inc) (Sum.Monoid.empty); // yields 1 ((0 + 1) * (0 + 1)) instead of 4 ((1 + 1) * (1 + 1))
在 Haskell 中,每个实例都有自己独特的类型来防止这种无意义的表达。但是必须从一种类型包装器转换为另一种类型包装器可能很乏味。
是否有可行的、类型安全的替代方案,或者这就是 Haskell 的设计者选择不同类型每个类实例的原因吗?