是否可以拥有“本地”类型类实例?

时间:2012-03-03 11:44:59

标签: haskell typeclass

我的意思是定义一个类型类的实例,该实例应用于函数中的本地(letwhere)范围。更重要的是,我希望在这个实例中的函数是闭包,即能够关闭定义实例的词法范围中的变量(这意味着实例在下次调用它的函数时可能会有不同的工作方式)。

我可以为您提供一个简化的用例。假设我有一个基于类型类操作的函数。在这个例子中,我使用平方,它对任何类型的实例Num进行操作(是的,平方非常简单并且可以很容易地重新实现,但是它代表了一些可能更复杂的东西)。我需要能够按原样使用现有的功能(不改变它或重新实现它)。

square :: Num a => a -> a
square x = x * x

现在,假设我希望在模运算中使用此运算,即加法,乘法等等。这对于任何固定的模数基都很容易实现,但是我希望有一些通用的东西,我可以为不同的模数基重复使用。我希望能够定义这样的东西:

newtype ModN = ModN Integer deriving (Eq, Show)

-- computes (x * x) mod n
squareModN :: 
squareModN x n =
  let instance Num ModN where
    ModN x * ModN y = ModN ((x * y) `mod` n) -- modular multiplication
    _ + _ = undefined         -- the rest are unimplemented for simplicity
    negate _ = undefined
    abs _ = undefined
    signum _ = undefined
    fromInteger _ = undefined
  in let ModN y = square (ModN x)
     in y

关键在于我需要使用上面的函数(square),该函数要求其参数是一个类型,它是某个类型类的实例。我定义了一个新类型并使其成为Num的实例;但是,为了正确执行模运算,它取决于模数基n,由于此函数的通用设计,它可能会在调用之间发生变化。我希望将实例函数定义为square函数的一次性“回调”(如果你愿意),以定制它此次执行操作的方式(并且只是这次)。

一种解决方案可能是将“闭包变量”直接集成到数据类型本身(即ModN (x, n)以表示它所属的数字和基数),操作只能从参数中提取此信息。但是,这有几个问题:1)对于多参数函数(例如(*)),它需要在运行时检查这些信息是否匹配,这很难看; 2)实例可能包含0参数“值”,我可能希望依赖于闭包变量,但由于它们不包含参数,因此无法从参数中提取它们。

1 个答案:

答案 0 :(得分:12)

拟议的扩展在我的this previous answer中表现出同样的问题;您可以使用本地实例创建两个具有相同密钥类型但不同Map个实例的Ord,从而导致所有不变量崩溃。

但是,reflection包允许您定义这样的ModN类型:您使用Reifies约束定义单个实例,并激活特定 n的实例 reify。 (我相信implicit parameters也会使这成为可能,但很少使用这种扩展。)