【问题标题】:Haskell: class instance with class type parametersHaskell:具有类类型参数的类实例
【发布时间】:2014-09-10 15:01:29
【问题描述】:

美好的一天。我是 Haskell 的新手。关于声明和实例化一些自定义类,我不清楚一件事。

  1. haskell 中有一个标准类Integral。根据hackage,Integral声明了强制方法quot :: a -> a -> a。所以这意味着该类的每个实例都应该有这个方法实现,对吧?

  2. 我们可以声明一些函数,使用 Integral 作为参数,例如:

proba :: (Integral a) => a -> a -> a
proba x y = x `quot` y

到目前为止一切顺利

  1. 现在让我们声明我们自己的 Proba 类:
class Proba a where
    proba :: a -> a -> a

我可以像这样实现 Int 或 Integer(或其他数据类型)实例:

instance Proba Integer where
    proba x y = x `quot` y

instance Proba Int where
    proba x y = x `quot` y

但我不想。 我希望每个 Integral 都有一个实例。但是当我尝试这样做时,我得到一个错误:

instance (Integral a) => Proba a where
    proba x y = x `quot` y

 Illegal instance declaration for `Proba a'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are *distinct type variables*,
    and each type variable appears at most once in the instance head.
    Use FlexibleInstances if you want to disable this.)
 In the instance declaration for `Proba a'

好的,它似乎要求我提供不同类型的变量,而不是类。但为什么?!为什么仅仅在这里有一个Integral 还不够?因为quot 是为每个Integral 声明的,所以这个实例应该对每个Integral 都有效,不是吗?

也许有办法达到同样的效果?

【问题讨论】:

  • Use FlexibleInstances if you want to disable this.你试过吗?
  • 我肯定会这样做,但我想知道为什么这些东西隐藏在某些自定义选项后面并且默认情况下不可用?
  • 因为原因;这在技术上是对语言默认工作方式的扩展。您可以阅读所有这些here

标签: haskell


【解决方案1】:

如错误消息所示,您可以使用FlexibleInstances(一个相当常见且安全的扩展名)来允许此行为,但您还需要UndecidableInstances

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

class Proba a where
    proba :: a -> a -> a

instance Integral a => Proba a where
    proba = quot

默认情况下不启用它的原因是因为它是专门的 GHC 扩展,而不是 Haskell98 规范的一部分。您会发现有很多语言扩展非常有用且使用安全,而且通常您只希望在特定模块中启用它们。不仅要问“为什么不是默认设置”,还要问“我什么时候不希望这是默认设置?”。


另一种无需扩展的实现方式是将类型类直接编码为数据类型:

data Proba a = Proba
    { proba :: a -> a -> a
    }

integralProba :: Integral a => Proba a
integralProba = Proba quot

然后你可以把它传递为

foldProba :: Proba a -> a -> [a] -> a
foldProba p = foldr (proba p)

如果你有foldProba integralProba,那么它会自动将类型限制为Integral a => a -> [a] -> a

【讨论】:

  • 非常感谢您,现在一切都清楚了。关于“我什么时候不希望这是默认设置?”这个问题。 - 好吧,我认为这很常见:如果我们可以用函数做这样的魔法,那么为什么我们不能用类做同样的事情呢?无论如何 - 这个解决方案对我有好处。
  • @skapral 这些不是一回事。使用Integral a => Proba a 实例,您还可以定义一个特定的Proba Int 实例,其中proba 的行为与使用IntIntegral 实例的行为不同。这就是UndecidableInstances 的用途。
  • @skapral 查看我的编辑以获取替代实现,它可以让您避免语言扩展并仅使用 Haskell98 代码来执行此操作。然后你可以很容易地拥有integralProbaintProbaintegerProba 等等,它们都可以根据需要具有不同的行为,甚至intProba1intProba2 具有Proba Int 类型但不同的行为。
  • 我不知道现在可以做到这一点。感谢您的信息。
  • @MathematicalOrchid this talk SPJ 很好地解释了类型类的工作原理,基本上所有类型类都充当编译器传入的隐式参数。此参数只是类型类中所有函数的向量表,因此您将拥有IntNum 实例的num_Int 向量表,依此类推。然而,虽然类型类在 Haskell 的大部分内容中都非常有用,但我发现越来越多地使用它们而不是仅仅使用函数表。
猜你喜欢
  • 2017-04-11
  • 1970-01-01
  • 2016-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
相关资源
最近更新 更多