icz*_*icz 4 haskell typeclass type-families
一段时间以来,我一直在努力在Haskell的类型系统中表达某种模式.我甚至很难解释它(我想我不能很好地理解它的问题),但是这里去了!(并提前感谢任何能够理解并回答我的问题的勇敢的冒险家!)
让我们假设我们有两种不同的容器类型,ContF并且ContD每种容器类型只是一个ADT ,它们分别保持单个值Float和Double(在这个例子中它们也可能是新类型).
我们想要编写一个适用于任何一种类型的多态函数.也许它提取值并将其加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
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试编译它时,我收到以下错误.
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
Run Code Online (Sandbox Code Playgroud)
也许我想问的是:如何创建一个类型类来表示包含可提取Num实例类型的容器?另外,请注意,这是一个非常人为的例子来展示我希望理解的内容.
答案在错误消息中.你需要以某种方式告诉编译器n实际上ContNum.
你可以做
contAdd1:: (Cont t, Num n, n ~ ContNum t) => t -> n
Run Code Online (Sandbox Code Playgroud)
或者不使用n在所有
contAdd1 :: (Cont t, Num (ContNum t)) => t -> ContNum t
Run Code Online (Sandbox Code Playgroud)
但最后一个需要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
Run Code Online (Sandbox Code Playgroud)