简体   繁体   English

我是否正在考虑并正确使用Haskell中的单例类型?

[英]Am I thinking about and using singleton types in Haskell correctly?

I want to create several incompatible, but otherwise equal, datatypes. 我想创建几个不兼容但相同的数据类型。 That is, I'd like to have a parameterized type Foo a , and functions such as 也就是说,我想要一个参数化类型Foo a ,以及诸如的函数

bar :: (Foo a) -> (Foo a) -> (Foo a) 

without actually caring about what a is. 实际上不关心什么a是。 To clarify further, I'd like the type system to stop me from doing 为了进一步澄清,我希望类型系统阻止我做

x :: Foo Int
y :: Foo Char
bar x y

while I at the same time don't really care about Int and Char (I only care that they're not the same). 虽然我同时并不真正关心IntChar (我只关心他们不一样)。

In my actual code I have a type for polynomials over a given ring. 在我的实际代码中,我有一个给定环上的多项式类型。 I don't actually care what the indeterminates are, as long as the type system stops me from adding a polynomial in t with a polynomial in s. 我实际上并不关心不确定性是什么,只要类型系统阻止我在t中用多项式添加多项式。 So far I've solved this by creating a typeclass Indeterminate , and parameterizing my polynomial type as 到目前为止,我已经通过创建一个类型类Indeterminate ,并将我的多项式类型参数化为...来解决这个问题

data (Ring a, Indeterminate b) => Polynomial a b

This approach feels perfectly natural for the Ring part because I do care about which particular ring a given polynomial is over. 对于Ring部分,这种方法感觉非常自然,因为我确实关心给定多项式结束的特定环。 It feels very contrived for the Indeterminate part, as detailed below. 对于Indeterminate部分感觉非常有用,详情如下。

The above approach works fine, but feels contrived. 上述方法工作正常,但感觉做作。 Especially so this part: 特别是这部分:

class Indeterminate a where
    indeterminate :: a

data T = T

instance Indeterminate T where
    indeterminate = T

data S = S

instance Indeterminate S where
    indeterminate = S

(and so on for perhaps a few more indeterminates). (等等或许还有一些不确定的)。 It feels weird and wrong. 这感觉奇怪和错误。 Essentially I'm trying to demand that instances of Indeterminate be singletons (in this sense ). 本质上,我试图要求Indeterminate实例是单身人士(在这个意义上 )。 The feeling of weirdness is one indicator that I might be attacking this wrongly. 奇怪的感觉是我可能会错误地攻击这个问题的一个指标。 Another is the fact that I end up having to annotate a lot of my Polynomial ab s since the actual type b often cannot be inferred (that's not strange, but is annoying nevertheless). 另一个原因是我最终不得不注释我的Polynomial ab因为实际的类型b通常无法推断(这并不奇怪,但仍然很烦人)。

Any suggestions? 有什么建议? Should I just keep on doing it like this, or am I missing something? 我应该继续这样做,还是我错过了什么?

PS: Don't feel offended if I don't upvote or accept answers immediately. PS:如果我不立即投票或接受答案,请不要觉得被冒犯。 I'll be unable to check back in for a few days. 我将无法再回来查看几天。

First of all, I'm not sure this: 首先,我不确定这个:

data (Ring a, Indeterminate b) => Polynomial a b

...is doing what you expect it to. ......正在做你期望的事情。 Contexts on data definitions are not terribly useful--see the discussion here for some reasons why, most of which amount to them forcing you to add extra annotations without actually providing many additional type guarantees. 关于data定义的上下文并不是非常有用 - 请参阅此处的讨论 ,原因可能就是为什么,其中大部分都是强制它们强制您添加额外的注释而不实际提供许多其他类型保证。

Second, do you actually care about the "indeterminate" parameter other than to ensure that the types are kept distinct? 其次,除了确保类型保持不同之外,你真的关心“不确定”参数吗? A pretty standard way of doing that sort of thing is what's called phantom types --essentially, parameters in the type constructor that aren't used in the data constructor. 执行此类事情的一种非常标准的方法是所谓的幻像类型 - 实际上,类型构造函数中的参数未在数据构造函数中使用。 You'll never use or need a value of the phantom type, so functions can be as polymorphic as you want, eg: 您永远不会使用或需要幻像类型的值,因此函数可以像您想要的那样具有多态性,例如:

data Foo a b = Foo b

foo :: Foo a b -> Foo a b
foo (Foo x) = Foo x

bar :: Foo a c -> Foo b c
bar (Foo x) = Foo x

baz :: Foo Int Int -> Foo Char Int -> Foo () Int
baz (Foo x) (Foo y) = Foo $ x + y

Obviously this does require annotations, but only in places where you're deliberately adding restrictions. 显然这确实需要注释,但仅限于您故意添加限制的地方。 Otherwise, inference will work normally for the phantom type parameter. 否则,推理将对幻像类型参数正常工作。

It seems to me that the above approach should be sufficient for what you're doing here--the business with singleton types is mostly about bridging the gap between more complicated type-level stuff and regular value-level computations by creating type proxies for values. 在我看来,上述方法应该足以满足您在这里所做的事情 - 单例类型的业务主要是通过创建值的类型代理来弥合更复杂的类型级别的东西和常规的价值级别计算之间的差距。 。 This could be useful for, say, marking vectors with types that indicate their basis, or marking numeric values with physical units--both cases where the annotation has more meaning than just "an indeterminate called X". 例如,这可以用于标记具有指示其基础的类型的向量,或用物理单位标记数值 - 这两种情况下注释具有更多意义而不仅仅是“不确定的称为X”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM