我的意思是定义一个类型类的实例,该实例应用于函数中的本地(let
或where
)范围。更重要的是,我希望在这个实例中的函数是闭包,即能够关闭定义实例的词法范围中的变量(这意味着实例在下次调用它的函数时可能会有不同的工作方式)。
我可以为您提供一个简化的用例。假设我有一个基于类型类操作的函数。在这个例子中,我使用平方,它对任何类型的实例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参数“值”,我可能希望依赖于闭包变量,但由于它们不包含参数,因此无法从参数中提取它们。
答案 0 :(得分:12)
拟议的扩展在我的this previous answer中表现出同样的问题;您可以使用本地实例创建两个具有相同密钥类型但不同Map
个实例的Ord
,从而导致所有不变量崩溃。
但是,reflection包允许您定义这样的ModN
类型:您使用Reifies
约束定义单个实例,并激活特定 n的实例 reify
。 (我相信implicit parameters也会使这成为可能,但很少使用这种扩展。)