繁体   English   中英

如何创建一个类型类来表示包含可提取的`Num`实例类型的容器?

[英]How can I make a typeclass to represent containers containing extractable `Num` instance types?

一段时间以来,我一直在努力在Haskell的类型系统中表达某种模式。 我甚至很难解释它(我想我不能很好地理解它的问题),但是这里去了! (并提前感谢任何能够理解并回答我的问题的勇敢的冒险家!)

让我们假设我们有两种不同的容器类型, ContFContD ,每种容器类型只是一个ADT,它分别保持一个FloatDouble值(在这个例子中它们也可能是newtypes)。

我们想要编写一个适用于任何一种类型的多态函数。 也许它提取值并将其加1(所以我们有一个Num约束)。 因此,我们创建一个类型类Cont ,其中包含一个用于提取值的函数。

我们不能只将num原型作为num :: (Num n) => t -> n ,因为那样我们就会得到“无法推断Float~n”的编译时错误; 所以我们介绍一个相关的类型系列。

{-# LANGUAGE TypeFamilies #-}

data ContF = CF Float
data ContD = CD Double

class Cont t where
    type family ContNum t :: *
    num :: t -> ContNum t

instance Cont ContF where
    type ContNum ContF = Float
    num (CF f) = f

instance Cont ContD where
    type ContNum ContD = Double
    num (CD d) = d

contAdd1 :: (Cont t, Num n) => t -> n
contAdd1 t = num t + 1

但是,当我尝试编译它时,我收到以下错误。

Could not deduce (n ~ ContNum t)
from the context (Cont t, Num n)
  bound by the type signature for contAdd1 :: (Cont t, Num n) => t -> n

也许我想问的是:如何创建一个类型类来表示包含可提取的Num实例类型的容器? 另外,请注意,这是一个非常人为的例子来展示我希望理解的内容。

答案在错误消息中。 你需要以某种方式告诉编译器n实际上是ContNum

你可以做

contAdd1:: (Cont t, Num n, n ~ ContNum t) => t -> n

或者根本不使用n

contAdd1 :: (Cont t, Num (ContNum t)) => t -> ContNum t

但最后一个需要FlexibleContexts扩展。

这是你问题的第一部分,如何编译。 要回答有关如何在类型级别移动约束的问题? 只需添加它。 有用 :-)

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

data ContF = CF Float
data ContD = CD Double

class Num (ContNum t) =>  Cont t where
    type family ContNum t :: *
    num :: t -> ContNum t

instance Cont ContF where
    type ContNum ContF = Float
    num (CF f) = f

instance Cont ContD where
    type ContNum ContD = Double
    num (CD d) = d

contAdd1 :: (Cont t) => t -> ContNum t
contAdd1 t = num t + 1



> contAdd1 (CF 4)
 5.0

不知道如何与他们新奇的家庭一起做这件事,但有了fundeps这一切都很容易。

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

data ContF = CF Float
data ContD = CD Double

class Num n => Cont t n | t → n where
    num :: t → n

instance Cont ContF Float where
    num (CF f) = f

instance Cont ContD Double where
    num (CD d) = d

contAdd1 :: (Cont t n) => t → n
contAdd1 t = num t + 1

我怀疑类型系列应该比fundeps更严格,但我不太了解它们。

暂无
暂无

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

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