2011-10-22 64 views
5

为了简单起见,我将使用这个人为的例子类(的一点是,我们从方法得出一些昂贵的数据):记忆化和类型类

class HasNumber a where 
    getNumber :: a -> Integer 
    getFactors :: a -> [Integer] 
    getFactors a = factor . getNumber 

当然,我们可以做memoizing实现这个类如:

data Foo = Foo { 
    fooName :: String, 
    fooNumber :: Integer, 
    fooFactors :: [Integer] 
} 

foo :: String -> Integer -> Foo 
foo a n = Foo a n (factor n) 

instance HasNumber Foo where 
    getNumber = fooNumber 
    getFactors = fooFactors 

但它似乎是需要有点难看的“因素”字段手动添加到,这将是一个HasNumber实例的任何记录。下一步的想法:

data WithFactorMemo a = WithFactorMemo { 
    unWfm :: a, 
    wfmFactors :: [Integer] 
} 

withFactorMemo :: HasNumber a => a -> WithFactorMemo a 
withFactorMemo a = WithFactorMemo a (getFactors a) 

instance HasNumber a => HasNumber (WithFactorMemo a) where 
    getNumber = getNumber . unWfm 
    getFactors = wfmFactors 

这将需要大量的样板的原始a的所有其他业务提升为WithFactorMemo a,虽然。

是否有任何优雅的解决方案?

+0

我只是想到了另外一个解决办法是让*因素*功能memoizing,虽然这将是不太实际的,如果getNumber'的'结果是一些比较大的数据结构,(据我所知)的条目将永远不会被垃圾收集(与我的问题中的两个解决方案相反)。 – FunctorSalad

回答

7

这里的解决方案:失去类型类。我已经谈到了这个herehere。任何类型类别TC a,其每个成员都将单个a作为参数与数据类型同构。这意味着,你HasNumber类的每个实例可以在这个数据类型来表示:

data Number = Number { 
    getNumber' :: Integer, 
    getFactors' :: [Integer] 
} 

也就是说,通过这种转变:

toNumber :: (HasNumber a) => a -> Number 
toNumber x = Number (getNumber x) (getFactors x) 

而且Number显然是HasNumber实例为好。

instance HasNumber Number where 
    getNumber = getNumber' 
    getFactors = getFactors' 

这同构向我们展示了这个类是一种变相的数据类型,它应该死。改用Number。如何做到这一点可能最初并不明显,但有一点经验应该会很快出现。 。例如,您Foo类型变为:然后

data Foo = Foo { 
    fooName :: String, 
    fooNumber :: Number 
} 

你的memoization会自由,因为因子存放在Number数据结构。

+0

实际上,这就是我刚刚发布之前就决定尝试的东西:)我同意将操作放入单一类型(此处为'Number'),但是也许有一个'HasNumber a'类仍然是个好主意numberDict :: a - > Number'连同包装器'getNumber = getNumber'。 numberDict'等等。但是我们必须将'Number'存储在要成为'HasNumber'的记录中,而不是在'numberDict'实现中从Integer创建'Number'(这当然会使我们不再记忆) 。 – FunctorSalad

+0

在这种情况下,我强烈建议不要使用类型类,它只会阻碍你。具体来说,FP工具箱更适合于这种编程,这种语言比抽象类更好地抽象数据类型,它不会让你自己欺骗你正在做OO建模(你是不是 - 如果你这样想,即使没有意识到,语言最终会限制你的道路)。 – luqui