【问题标题】:Haskell Typeclass Type Constraints and DeductionHaskell Typeclass 类型约束和推导
【发布时间】:2013-08-19 12:54:38
【问题描述】:

我一直在阅读“Learn You a Haskell”一书,我正试图围绕 Haskell 类型类展开思考。作为实践,我正在尝试创建一个简单的矢量类型类。下面的 sn-p 代码让我有些悲痛(导致我在 StackOverflow 上的第一篇文章):

data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)

class Vector a where
    (<*) :: (Num b) => a -> b -> a

instance (Num a) => Vector (Vec2 a) where
    Vec2 (x,y) <* a = Vec2 (a*x, a*y)

我收到以下错误消息:

Could not deduce (a~b) from the context (Num a) or from (Num b) bound by the type signature for
    <* :: Num b => Vec2 a -> b -> Vec2 a

似乎在类型类中指定的Num 应该提供a 的类型,而实例中的Num a 规范应该提供xy 的类型,那么它为什么会抱怨?我对这段代码有什么误解?

【问题讨论】:

  • 提示,b 是普遍量化的。
  • 提示,(*) :: Num a =&gt; a -&gt; a -&gt; a,因为 b 是通用量化的,当你使用 * 时,编译器无法推导出 a~b

标签: haskell typeclass


【解决方案1】:

我认为问题在于 (*) 的类型为 (Num a) =&gt; a -&gt; a -&gt; a 而不是编译器预期的 (Num a, Num b) =&gt; a -&gt; b -&gt; a

我不熟悉 Haskell 的数字转换,但我的解决方法有限,因为第二个参数是 Integral 的实例。

data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)    

class Vector a where
    (<*) :: (Integral b) => a -> b -> a

instance (Num a) => Vector (Vec2 a) where
    Vec2 (x,y) <* a = Vec2 (x*b, y*b)
        where b = fromIntegral a

因为fromIntegral的类型为(Integral a, Num b) =&gt; a -&gt; b,所以可以根据需要转换*的第二个参数。

【讨论】:

  • 4.2相乘怎么样?
  • @Satvik 它仅在 Vec2 与 Integrals 相乘时才有效,我希望有一个 (Num a, Num b) =&gt; a -&gt; b -&gt; a 类型的函数来代替 * 但我不知道如何实现它。跨度>
【解决方案2】:

(*) :: Num a =&gt; a -&gt; a -&gt; a 的类型。但是,当您实际尝试使用* 时,实际上是在将两个具有Num 实例的不相关类型相乘,并且编译器无法推断它们是相同的。

为了更清楚地解释它,请查看&lt;* 的类型和b 的通用量化

(<*) :: (Num b) => a -> b -> a

你在这里说的是,给我任何具有Num 实例的类型,我可以将它与我的向量相乘,但你想说的是不同的东西。

你需要知道如何说Vec2 a 类型中的a(&lt;*) :: Num b =&gt; a -&gt; b -&gt; a 类型中的b 相同,只有这样你才能将它们相乘。这是一个使用类型族来确保这种约束的解决方案。

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}

data Vec2 a = Vec2 (a,a) deriving (Show, Eq, Read)

class (Num (VectorOf a)) => Vector a where
    type VectorOf a :: *
    (<*) :: a -> (VectorOf a) -> a

instance (Num a) => Vector (Vec2 a) where
    type VectorOf (Vec2 a) = a
    Vec2 (x,y) <* a = Vec2 (a*x, a*y)

【讨论】:

    【解决方案3】:

    编译器无法验证所涉及的两个Num 实例实际上是同一类型。它们都是Num 实例,当然,但还需要它们必须是 same 实例。

    否则,你可以这样写:

    Vec2 (1 :: Double, 2 :: Double) <* (3 :: Int)
    

    到了该做的时候它不会飞,例如:(1 :: Double) * (3 :: Int)

    【讨论】:

      猜你喜欢
      • 2013-08-15
      • 2013-05-31
      • 1970-01-01
      • 2012-03-21
      • 2015-09-06
      • 1970-01-01
      • 2013-11-05
      • 2021-06-23
      • 2021-03-18
      相关资源
      最近更新 更多