haskell - 如何为 Haskell 实例赋予具体值?
问题描述
我正在尝试学习 Haskell,但我对这个臭名昭著的“刚性类型变量”错误感到有些困惑。
这是我的代码的相关部分:
class Ord v => Vector v where
distance :: v -> v -> Double
-- with FlexibleInstances
instance Vector (Double, Double) where
distance (a,b) (c,d) = -- function definition omitted
data KMeansState v = KMeansState { centroids :: [v] }
test :: [(Double,Double)]
test = [(0,0),(1,1)] :: [(Double,Double)]
initializeState :: Vector v => Int -> KMeansState v
initializeState n = KMeansState test
编译器给我的具体投诉:
• Couldn't match type ‘v’ with ‘(Double, Double)’
‘v’ is a rigid type variable bound by
the type signature for:
initializeState :: forall v.
Vector v =>
Int -> Double -> KMeansState v
at Chapter06.hs:32:1-61
Expected type: KMeansState v
Actual type: KMeansState (Double, Double)
我的理解是编译器说我的initializeState
函数需要能够返回(或 Haskell 中的任何正确词),KMeansState
其类型参数是任何类型的实例Vector
,而我的函数的实际实现只会返回一种类型,即一个KMeansState (Double, Double)
。
我不清楚的是如何从这个initializeState
函数中得到一个“具体”的值。在程序中的某个时刻,我需要给KMeansState
构造函数一个实际值,该值将具有实际类型,并且编译器似乎在说我不能这样做。
如果我将test
函数的值硬编码到initializeState
. 也就是说,这成功地进行了类型检查,即使传递给KMeansState
函数的值在功能上应该是相同的。
class Ord v => Vector v where
distance :: v -> v -> Double
-- with FlexibleInstances
instance Vector (Double, Double) where
distance (a,b) (c,d) = -- function definition omitted
data KMeansState v = KMeansState { centroids :: [v] }
-- test :: [(Double,Double)]
-- test = [(0,0),(1,1)] :: [(Double,Double)]
initializeState :: Vector v => Int -> KMeansState v
initializeState n = KMeansState [(0,0),(1,1)]::[(Double,Double)]
有人可以澄清这里发生了什么以及我如何编译它吗?谢谢!
解决方案
这是对 Haskell 类型类是什么/它们如何工作的常见的基本误解。具体来说,Haskell 类不像OO 语言中的类。它在某些方面就像一个接口/抽象类,但也不是真的。
特别是,您不能拥有“类的具体值”。类没有值,它们有实例——但那些是类型,而不是值。(这些类型可能有也可能没有值——事实上,严格来说,它们根本不需要是类型,只是类型级别的实体。)
你打算用你对initializeState
函数的签名说的是“结果是一个类型的值KMeansState v
,其中v
某个类型是Vector
类的一个实例,但我不想告诉你是哪个”。它实际上说的是,“对于v
你选择的任何类型,只要它是Vector
类的一个实例,它就是一个initializeState
产生KMeansState v
值的函数”。由于“for any”,又名“forall”或∀,我们称其为普遍量化类型。
同时,预期的类型是存在量化的。为什么会这样称呼它有点奇怪;如果我将其表述为“结果是一个值r
,其中存在v
具有Vector
实例r
的类型,例如具有类型KMeansState v
”,这更容易理解。
虽然 Haskell 一直都有普遍量化的类型,但它并没有真正存在的类型。您可以做的是将存在包装在自定义类型中:
{-# LANGUAGE GADTs #-}
data SomeVectorMeanState where
SomeVectorMeanState :: Vector v => KMeansState v -> SomeVectorMeanState
initializeState :: Int -> SomeVectorMeanState
initializeState n = SomeVectorMeanState $ KMeansState test
但这并没有真正完成任何有用的事情。事实上,您将无法以任何方式使用向量值,因为具体类型是未知的。见https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/
相反,正如 Willem Van Onsem 评论的那样,您可能应该只使用
initializeState :: Int -> KMeansState (Double,Double)
或者,如果您不想让向量类型如此明显,则(Double,Double)
可以将其包装在 a type
ornewtype
中。
推荐阅读
- java - 无法为值 [null] 转换参数 [model],原因是:无法构造 `view.model.product.ProductViewModel` 的实例
- codenameone - 如何在代号中创建圆形容器?
- javascript - 指数据的firestore安全
- xamarin.forms - Android 相机照片不应存储在设备和 SD 卡中
- python - re.sub 带括号,删除日文红宝石字符
- c# - C# 每 5 秒读取一次 SQLite 数据库 (WPF)
- c# - 如何在 C# 中使用 SHA2 正确签署 SOAP 消息?
- java - 导出到 jar 时缺少 Eclipse 中可用的库
- python - 如何使用 Python 高效地将大型 CZI 图像(+50GB)转换为 JP2?
- python - 在序列化程序中过滤嵌套查询集