【问题标题】:return type of GADT evaluatorGADT 评估器的返回类型
【发布时间】:2015-01-22 21:05:30
【问题描述】:

我有点不知道为什么 prodV 中的最后一个模式在下面不起作用:

 {-# LANGUAGE GADTs #-}

data V a where
  V0 :: Float -> V Float
  Vn :: [Float] -> V [Float]

prodV :: (Num a) => V a -> V a -> a
prodV (V0 x) (V0 y) =  x * y
-- prodV (Vn x) (Vn y) = zipWith (*) x y -- this one works
prodV (Vn xs) (Vn ys) = [ sum $ zipWith (*) xs ys ]
prodV (V0 n) (Vn ys) = map (* n) ys 

GHCi 7.8.3 抱怨:

Couldn't match type ‘Float’ with ‘[Float]’ 
Inaccessible code in
  a pattern with constructor
    Vn :: [Float] -> V [Float],
  in an equation for ‘prodV’
In the pattern: Vn ys`

有什么建议吗?提前致谢

【问题讨论】:

  • 一方面,您希望您的函数 prodV 适用于所有 Num 类型,但 V0Vn 构造函数特别包含 Float。其次,您是说V a 中的a 必须是Num,但Num a => [a] 不是Num 的实例。看起来您以错误的方式处理此问题。
  • 您当然可以编写像unV :: V a -> a 这样的函数来简单地解开构造函数,但您的函数的前提似乎不正确。也许您应该考虑Vn :: [Float] -> V Float,但这将使unV :: V a -> a 成为不可能,除非您首先以某种方式将Floats 列表转换为单个Float
  • 即使我删除了 Num 约束,错误仍然存​​在;我不太明白为什么 V0 和 Vn 构造函数不能“混合”..
  • @ocramz V0 n 的类型为 V Float,而 Vn ns 的类型为 V [Float]。它们不是相同的类型,但您在 prodV 的类型中指定它们必须相同。 GADT 构造函数上的模式匹配优化类型。
  • 您的函数需要使用Num 函数,例如*sum,因此您在类型签名中指定了您想要的Num a => V a。但是V 的两个构造函数都是存在的,它们不能只保存任何类型的值。 V0Vn 中包含的值不只是任何 Num a => a,它们必须具有特定的类型。您不能使用V0Float 以外的任何值来构造值。同样,Vn 必须使用 [Float] 类型的值构造。您试图告诉编译器 V0 xVn yxy 具有相同的类型,但根据定义它们不能。

标签: haskell gadt


【解决方案1】:

签名

prodV :: (Num a) => V a -> V a -> a

要求两个参数具有相同的类型参数,但如果您在 V0Vn 上匹配,则参数必须具有类型参数不匹配的 V FloatV [Float] 类型。

我不太确定您希望语义是什么,但我猜您想将 GADT 定义为类似

data V n where
  V0 :: n -> V n
  Vn :: [n] -> V n

以及你的功能

prodV :: (Num a) => V a -> V a -> V a
prodV (V0 x) (V0 y) =  V0 $ x * y
prodV (Vn xs) (Vn ys) = Vn $ zipWith (*) xs ys
prodV (V0 n) (Vn ys) = Vn $ map (* n) ys

或者可能

prodV' :: (Num a) => V a -> V a -> a
prodV' (V0 x) (V0 y) =  x * y
prodV' (Vn xs) (Vn ys) = sum $ zipWith (*) xs ys
prodV' (V0 n) (Vn ys) = sum $ map (* n) ys

您能描述一下您希望函数prodV 实际执行的操作吗?原始代码中的类型对我来说并没有真正的意义。

【讨论】:

    【解决方案2】:

    好吧,GHC 会准确地告诉您问题所在。根据您的定义,V0 something 始终具有 V Float 类型(并且 something 应该是 Float 类型——但这在这里不相关),而 Vn anything 始终具有 V [Float] 类型。但是prodV 的类型签名指定两者具有相同的类型V a。所以,在这种情况下a 应该同时是Float[Float],这是不可能的。

    【讨论】:

      【解决方案3】:

      我只是想了解如何重载函数以模仿代数速记,但我想这不是正确的途径。 惯用的 Haskell 方法是什么?例如。来自Numeric.Matrix

      adaptScalar f1 f2 f3 x y
          | dim x == 1 = f1   (x@>0) y
          | dim y == 1 = f3 x (y@>0)
          | otherwise = f2 x y
      
      instance Num (Vector Float) where
          (+) = adaptScalar addConstant add (flip addConstant)
          negate = scale (-1)
          (*) = adaptScalar scale mul (flip scale)
          signum = vectorMapF Sign
          abs = vectorMapF Abs
          fromInteger = fromList . return . fromInteger
      

      根据操作数的维度选择运算符的正确实现(例如 + 或 *)。

      【讨论】:

      • 发布您自己问题的答案并不是更新问题的好方法。提出一个新问题或编辑您原来的问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      相关资源
      最近更新 更多