记忆和类型

时间:2011-10-22 17:18:05

标签: haskell memoization

为了简单起见,我将使用这个人为的示例类(重点是我们从方法中获得了一些昂贵的数据):

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

当然,我们可以记住这个类的实现,例如:

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

有没有优雅的解决方案?

1 个答案:

答案 0 :(得分: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
}

您的备忘录将免费提供,因为这些因素存储在Number数据结构中。