开始Haskell:一个基于项目....如何从(双,双)构造一个Vector

时间:2014-08-13 00:01:55

标签: haskell parameterized-types

我正在研究"开始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

我该怎么办?

1 个答案:

答案 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)

thanks to this thread for the idea