【问题标题】:Cannot seem to use a type expression with a type variable in an instance declaration with a function needing an explicit type似乎不能在实例声明中使用带有类型变量的类型表达式和需要显式类型的函数
【发布时间】:2019-04-15 05:55:31
【问题描述】:

我在 Haskell 中无论如何都找不到指定调用 'neg' 的类型:

instance Arith (V3 e) where neg x = vfmap (neg :: e->e)  x 

(V3 e)e 都是 Arith 的实例。这里我想调用已经为类型'e'定义的'neg'。但这需要 'neg' 调用上的显式类型,并且没有表达式可以解析该类型?如果使用 'e' 的特定实例,那就没问题了。

vfmap (neg :: Dist->Dist ) x -- 这行得通(但不够通用) vfmap (neg :: e->e) x -- (Arith e1) 没有因使用 ‘neg’ 而产生的实例 vfmap neg e -- 由使用“neg”引起的模棱两可的类型变量“e0” 防止约束“(Arith e0)”被解决。 vfmap (neg :: Arith e => e->e) x -- 同上

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, InstanceSigs #-}
data Dist = Inch Float deriving (Show)

class Arith a where
   neg :: a->a

instance Arith Dist where
   neg (Inch x) = Inch (-x)

data V2 e = V2 e e    deriving (Show) 
data V3 e = V3 e e e  deriving (Show)

class VMap c e where
   vfmap :: (e->e)->c->c

instance VMap (V2 e) e where
   vfmap f (V2 x1 x2) = V2 (f x1) (f x2)
instance VMap (V3 e) e where
   vfmap f (V3 x1 x2 x3) = V3 (f x1) (f x2) (f x3)

-- 2 & 3 point vectors should also be Arith
instance Arith (V2 Dist) where 
   neg x = vfmap (neg :: Dist->Dist) x -- works, but must have type on neg

instance Arith (V3 e) where 
   neg x = vfmap (neg :: Arith e => e->e)  x -- nothing here seems to work

vfmap 可以应用于 (V2 e) 或 (V3 e),任何一种向量类型都可以用于任何 Arith 元素类型。

当元素类型是类型变量时,这似乎无法编译,例如

• 由表达式类型签名产生的不明确类型变量“e0” 防止约束“(Arith e0)”被解决。 可能的解决方法:使用类型注释来指定“e0”应该是什么。

【问题讨论】:

    标签: haskell type-variables


    【解决方案1】:

    问题是在 Haskell 中,类型变量没有作用域:也就是说,如果你定义了instance Arith (V3 e),你就不能在实例内部使用e;如果您尝试这样做,GHC 会将其解释为完全独立的类型变量。幸运的是,您可以使用{-# LANGUAGE ScopedTypeVariables #-} 来启用作用域类型变量。如果你这样做,你还会发现你需要添加一个额外的Arith e => 约束;添加这将允许它成功编译。

    (旁白:在处理MultiParamTypeClasses 时,{-# LANGUAGE FunctionalDependencies #-} 也非常有用;我个人会在这种情况下使用它,因为它消除了对neg 的显式类型声明的需要。这个想法是你定义了class Functor c e | c -> e,这基本上意味着c的类型也决定了e的类型。我不会在这里描述它,但我会高度鼓励你看看起来。)

    【讨论】:

    • 是的!谢谢你。感谢您理解我的问题并提供建设性的反馈。这里真正的解决方案是“class Functor c e | c -> e”。作为参考,我实际上并没有尝试使用作用域类型变量。我习惯于在 C++ 背景下这样做,您可以在其中直接连接元素和元素类型的集合。所以,Haskell 让我很困惑。
    • @ErvanDarnell 不客气。 (另外,如果我的回答解决了你的问题,你能接受吗?一旦被接受,这个问题就会被标记为“已解决”。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 2021-06-19
    • 2023-03-11
    • 2012-06-30
    相关资源
    最近更新 更多