我正在研究"开始Haskell中的示例:基于项目的方法"在第6章中,你写了kMeans,在此期间我传入[(Double,Double)]并将它们转换为向量,生活是美好的。一切正常。
但是,我想在导入Vector模块后在ghci中创建一个向量,它会全部爆炸。
Prelude Chapter6.TimeMachine> import Chapter6.Vector
Prelude Chapter6.Vector Chapter6.TimeMachine> toVector ((0,0) :: (Double, Double))
<interactive>:15:1:
No instance for (Vectorizable (Double, Double) v0)
arising from a use of `toVector'
The type variable `v0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance Vectorizable (Double, Double) (Double, Double)
-- Defined in `Chapter6.Vector'
Possible fix:
add an instance declaration for (Vectorizable (Double, Double) v0)
In the expression: toVector ((0, 0) :: (Double, Double))
In an equation for `it': it = toVector ((0, 0) :: (Double, Double))
我想这都是由Vector定义的方式引起的,但我不明白为什么。已经有一个实例实例Vectorizable(Double,Double)(Double,Double),所以我希望toVector能够处理(Double,Double)的输入。我确实创建了(Double,Double)列表作为我传递给kMeans的数据,并且它调用toVector没有问题,但是kMeans函数具有以下类型签名,它以某种方式解决了这个问题:
-- kMeans takes input data and computes the centroids based off
kMeans :: (Vector v, Vectorizable e v) => Int -> [e] -> [v]
kMeans numCentroids dataPoints = ...
我不知道如何解决这个问题。我尝试了各种各样的咒语而没有得到任何地方。
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, BangPatterns #-}
module Chapter6.Vector where
import Data.List
class (Ord v) => Vector v where
distance :: v -> v -> Double
centroid :: [v] -> v
isUndefined :: v -> Bool
instance Vector (Double, Double) where
isUndefined (a,b) = isNaN a || isNaN b
distance (a,b) (c,d) = sqrt $ (c-a)*(c-a) + (d-b)*(d-b)
centroid vs = let baseVal = (0,0,0) :: (Int, Double, Double)
(n',x',y') = foldl' (\(!n,!x,!y) (x2,y2) -> (n+1,x+x2,y+y2)) baseVal vs
in toVector (x' / fromIntegral n', y' / fromIntegral n')
class Vector v => Vectorizable e v where
toVector :: e -> v
instance Vectorizable (Double,Double) (Double,Double) where
toVector = id
我该怎么办?
答案 0 :(得分:4)
让我们看看你的错误:
No instance for (Vectorizable (Double, Double) v0)
arising from a use of `toVector'
The type variable `v0' is ambiguous
GHCi知道您正在尝试将(Double, Double)
转换为Vector
的实例,但它不确定是哪一个。
你可以通过几种方式解决这个问题。
如错误所示(Possible fix: add a type signature that fixes these type variable(s)
- &#34;修正&#34;如在&#34;修复到位&#34;不&#34;修复&#34;),最简单的方法是告诉它哪一个:
λ toVector ((0,0) :: (Double, Double)) :: (Double, Double)
(0.0,0.0)
为什么这个含糊不清的原因是您已经设置了Vectorizable
类型类来支持许多不同的实例,例如:
instance Vector () where
isUndefined () = True
distance () () = 0.0
centroid _ = ()
instance Vector Double where
isUndefined = isNaN
distance = (abs .) . subtract
centroid = uncurry (/) . foldl' (\(!x,!n) y -> (x+y,n+1)) (0,0)
instance Vectorizable (Double,Double) () where
toVector = const ()
instance Vectorizable (Double,Double) Double where
toVector = fst
这些都不是尚未,但它们可能是,并且编译器不想在模糊的情况下猜测你。
摆脱歧义的解决方案是使用FunctionalDependencies(aka FunDeps)或TypeFamilies使输出类型依赖于Vectorizable的输入类型:
e.g。
{-# LANGUAGE FunctionalDependencies #-}
-- ...
class Vector v => Vectorizable e v | e -> v where
toVector :: e -> v
或
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
-- ...
class Vector (Output e) => Vectorizable e where
type Output e
toVector :: e -> Output e
instance Vectorizable (Double,Double) where
type Output (Double, Double) = (Double, Double)
toVector = id
现在只需指定输入类型,因为上面给出的Vectorizable
的其他实例不再合法:
λ toVector ((0,0) :: (Double, Double))
(0.0,0.0)
如果你使用TypeFamilies
,你可以在输入和输出之间建立双射关系,这样就可以指定输出类型或输入类型
class (Output e ~ v, Input v ~ e, Vector v) => Vectorizable e v where
type Input v
type Output e
toVector :: e -> Output e
instance Vectorizable (Double,Double) (Double,Double) where
type Input (Double, Double) = (Double, Double)
type Output (Double, Double) = (Double, Double)
toVector = id
给出:
λ (toVector (0,0)) :: (Double, Double)
(0.0,0.0)
λ toVector ((0,0) :: (Double, Double))
(0.0,0.0)