进行现场数据类型操作的最佳方法是什么?

时间:2018-11-20 18:11:55

标签: haskell

说我有一个像这样的数据类型:

data CpuStatRow a = CpuStatRow
  { user :: a
  , nice :: a
  , system :: a
  , idle :: a
  , ioWait :: a
  , irq :: a
  , softIrq :: a
  , steal :: a
  }

来自解析Linux操作系统/proc/stat的CPU信息,其中每个数字字段都是自启动以来的累积值。因此,如果我想找出某个时期的值,我需要抓住before :: CpuStatRow Intafter :: CpuStatRow Int并进行现场差异(假设适当的语言扩展):

-- let's not worry about distinction between a raw value and a difference for now ...
type Diff = Int
getDiff :: CpuStatRow Int -> CpuStatRow Int -> CpuStatRow Diff
getDiff after before = CpuStatRow {..}
  where
    diffOn prj = prj after - prj before
    user = diffOn user
    nice = diffOn nice
    ... -- basically same for each field

我想知道有什么更好的办法:

  • 概括为其他阿拉伯语的功能(例如,将功能f :: a -> b -> c -> d提升到某些f' :: F a -> F b -> F c -> F d
  • 可以轻松处理任何数据类型,因为/proc中有许多累加值,因此应用上述相同方法并不有趣。我有Applicative的想法,但ghc似乎并不认为它“可推导”

1 个答案:

答案 0 :(得分:4)

如果要泛化为任何Arity和任何参数类型的函数,我认为Applicative是最好的选择。确实,您将无法派生它,但是至少可以实现一次pure(<*>),然后将其重用于所有功能,而不必为每个功能执行繁琐的工作。您也可以要求GHC为您派生Functor实例。

如果要使用“累积值”而不是常规函数,则可以考虑添加以下形式的Monoid(或仅Semigroup)实例

instance Semigroup a => Semigroup (CpuStatRow a) where
  (CpuStatRow x y z ...) <> (CpuStatRow x' y' z') = CpuStatRow (x <> x') (y <> y') (z <> z') ...

您可以想象这会很快使人疲倦,但是幸运的是,您可以根据应用程序来实现它-我们已经在节省时间!

instance Semigroup a => Semigroup (CpuStatRow a) where
  (<>) = liftA2 (<>)

instance Monoid a => Monoid (CpuStatRow a) where
  mempty = pure mempty