简体   繁体   English

Haskell类型类型约束和演绎

[英]Haskell Typeclass Type Constraints and Deduction

I've been running through the "Learn You a Haskell" book, and I'm trying to wrap my head around Haskell Type Classes. 我一直在阅读“Learn You a Haskell”一书,我正试图将我的脑袋包裹在Haskell Type Classes中。 As practice, I'm trying to create a simple vector type class. 作为练习,我正在尝试创建一个简单的矢量类型类。 The following snippet of code has been giving me some grief (resulting in my first post to StackOverflow): 以下代码片段给了我一些悲伤(导致我第一篇发布到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)

I get the following error message: 我收到以下错误消息:

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

It seems like the Num being specified in the typeclass should supply the type of a , and Num a spefication in the instance should supply the type of x and y , so why is it complaining? 这似乎是Num在类型类应该提供的类型指定a ,和Num a实例中应该提供的类型spefication xy ,那么为什么抱怨? What misconception do I have about this code? 我对这段代码有什么误解?

The type of (*) :: Num a => a -> a -> a . (*) :: Num a => a -> a -> a But when you are actually trying to use * , you are actually multiplying two unrelated types having Num instances and the compiler is not able to infer that they are same. 但是,当您实际尝试使用* ,实际上您将两个具有Num实例的不相关类型相乘,并且编译器无法推断它们是相同的。

To explain it more clearly look at the type of <* and the universal quantification of b 为了更清楚地解释它,请看<*的类型和b的通用量化

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

What you are saying here is, give me any type having Num instance and I will be able to multiply that with my vector, but what you want to say is something different. 你在这里说的是,给我任何类型的Num实例,我将能够将它与我的向量相乘,但你想说的是不同的东西。

You need to some how say that a in the type Vec2 a is same as b in the type (<*) :: Num b => a -> b -> a , only then you can multiply them together. 你需要知道a类型Vec2 a中的b与类型(<*) :: Num b => a -> b -> a ,然后才能将它们相乘。 Here is a solution using typefamilies to ensure this constraint. 这是一个使用typefamilies来确保这种约束的解决方案。

{-# 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)

The compiler can't verify that both of the Num instances involved are actually the same type. 编译器无法验证所涉及的两个Num实例实际上是否为同一类型。 They're each Num instances, sure, but what is also required is that they must be the same instance. 当然,它们都是Num实例,但是还需要它们必须是同一个实例。

Otherwise, you could write something like this: 否则,你可以写这样的东西:

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

which doesn't fly when it comes time to do, ex: (1 :: Double) * (3 :: Int) . 什么时候不能飞,ex: (1 :: Double) * (3 :: Int)

I think the problem is (*) has the type (Num a) => a -> a -> a rather than (Num a, Num b) => a -> b -> a that complier expected. 我认为问题是(*)的类型(Num a) => a -> a -> a而不是(Num a, Num b) => a -> b -> a complier expect。

I'm not familiar with Haskell's number conversion but I have a limited workaround, given the second argument is an instance of Integral . 我不熟悉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

because fromIntegral has type (Integral a, Num b) => a -> b , it can convert the second argument of * as needed. 因为fromIntegral有类型(Integral a, Num b) => a -> b ,它可以根据需要转换*的第二个参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM