我想创建几种不兼容但在其他方面相等的数据类型。也就是说,我想要一个参数化类型 Foo a
和诸如
bar :: (Foo a) -> (Foo a) -> (Foo a)
实际上并不关心a
是什么。为了进一步澄清,我希望类型系统阻止我这样做
x :: Foo Int
y :: Foo Char
bar x y
虽然我同时并不真正关心Int
和Char
(我只关心它们不一样)。
在我的实际代码中,我有一个给定环上多项式的类型。我实际上并不关心不定式是什么,只要类型系统阻止我将 t 中的多项式与 s 中的多项式相加即可。到目前为止,我已经通过创建类型类 Instituteate
并将多项式类型参数化为
data (Ring a, Indeterminate b) => Polynomial a b
对于Ring
部分来说,这种方法感觉非常自然,因为我确实关心给定多项式结束于哪个特定环。 不确定
部分感觉非常人为,如下详述。
上述方法效果很好,但感觉很做作。尤其是这部分:
class Indeterminate a where
indeterminate :: a
data T = T
instance Indeterminate T where
indeterminate = T
data S = S
instance Indeterminate S where
indeterminate = S
(也许还有一些不确定因素,依此类推)。感觉很奇怪而且错误。本质上,我试图要求 Inminated
的实例是单例(在 this sense 中)。奇怪的感觉是我可能错误地攻击这一点的一个迹象。另一个事实是,我最终不得不注释很多多项式 a b
,因为实际类型 b
通常无法推断(这并不奇怪,但仍然很烦人) )。
有什么建议吗?我应该继续这样做,还是我错过了什么?
PS:如果我没有立即投票或接受答案,请不要感到被冒犯。几天后我将无法重新登录。
最佳答案
首先,我不确定这一点:
data (Ring a, Indeterminate b) => Polynomial a b
...正在做您期望的事情。 data
定义的上下文并不是很有用 - 请参阅 the discussion here由于某些原因,其中大多数都迫使您添加额外的注释,而不实际提供许多额外的类型保证。
第二,除了确保类型保持不同之外,您是否真的关心“不确定”参数?做这类事情的一个非常标准的方法就是所谓的 phantom types --本质上,类型构造函数中的参数未在数据构造函数中使用。您永远不会使用或需要幻像类型的值,因此函数可以根据需要具有多态性,例如:
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
显然,这确实需要注释,但仅限于您故意添加限制的地方。否则,对于幻像类型参数,推理将正常进行。
在我看来,上述方法应该足以满足您在这里所做的事情 - 单例类型的业务主要是通过创建类型来弥合更复杂的类型级内容和常规值级计算之间的差距值的代理。例如,这对于用指示其基础的类型标记向量或用物理单位标记数值可能很有用 - 在这两种情况下,注释都比“一个名为 X 的不确定”具有更多含义。
关于haskell - 我是否正确地思考和使用 Haskell 中的单例类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4817080/