2012-03-03 101 views
17

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

我可以给你一个简单的用例。假设我有一个基于类型类的类型的函数。在这个例子中,我使用了平方,它可以对任何类型的实例进行操作,例如Num(是的,平方很简单,可以很容易地重新实现,但它可能会更复杂)。我需要能够按原样使用现有功能(无需更改或重新实现)。

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

现在,假设我希望在模运算中使用这个操作,即加法,乘法等mod某些数字。这对于任何固定的模基很容易实现,但我想要有一些通用的东西,我可以重新使用不同的基模。我希望能够来定义是这样的:

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参数的“值”,我可能想依赖于闭包变量,但是,由于它们不包含参数,因此无法从参数中提取它们。

+2

不,你不能有本地实例。对于模块化算法,Oleg Kiselyov和CC Shan已经在他们的论文“隐式配置”中解决了这个问题 - http://www.cs.rutgers.edu/~ccshan/prepose/prepose.pdf。我个人倾向于避免这种情况,并简单地为模块化的基础 - 即Z7,Z12做一个新的类型。 Conal Elliott提供了另一种选择替代类型的模式,请参阅CxMonoid的文章“适用性数据驱动计算” - http://conal.net/papers/data-driven/paper.pdf – 2012-03-03 12:42:52

+1

事实上,反射包本质上是Oleg纸的包装版本。 – ehird 2012-03-03 12:56:49

+0

关于Haskell的现状:没有。实例将随处随地都可以到达。关于未来对Haskell的可能调整:也许。关于使用你自己的机制,而不是typeclasses:是的。 – 2012-03-03 22:01:02

回答

11

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

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

相关问题