首页 > 解决方案 > 类子类结构的设计模式

问题描述

我的目标是以优雅和高性能的方式表示一组具有相似行为的类型。为了实现这一点,我创建了一个使用单一类型的解决方案,然后是一组执行模式匹配的函数。

我的第一个问题是:有没有办法如何使用单个类型类来表示相同的想法,而不是每个变体都有一个构造函数来拥有一个实现所述类型类的类型?

以下两种方法中的哪一种是: - Haskell 中更好地识别的设计模式?- 内存效率更高?- 性能更高?- 更优雅,为什么?- 代码的消费者更容易使用?

方法一:单一类型和模式匹配

假设有以下结构:

data Aggregate a
  = Average    <some necessary state keeping>
  | Variance   <some necessary state keeping>
  | Quantile a <some necessary state keeping>

它的构造函数不是公开的,因为这会暴露内部状态保持。相反,存在一组构造函数:

newAverage :: Floating a
  => Aggregate a
newAverage = Average ...

newVariance :: Floating a
  => Aggregate a
newVariance = Variance ...

newQuantile :: Floating a
  => a                     -- ! important, a parameter to the function
  -> Aggregate a
newQuantile p = Quantile p ...

创建对象后,我们可以执行两个功能:put将值放入其中,一旦我们满意,我们就可以get获取当前值:

get :: Floating a
  => Aggregate a
  -> Maybe a
get (Average <state>) = getAverage <state>
get (Variance <state>) = getVariance <state>
get (Quantile _ <state>) = getQuantile <state>

put :: Floating a
  => a
  -> Aggregate a
  -> Aggregate a
put newVal (Average <state>) = putAverage newVal <state>
put newVal (Variance <state>) = putVariance newVal <state>
put newVal (Quantile p <state>) = putQuantile newVal p <state>

方法 2:类型类和实例

class Aggregate a where
  new :: a
  get :: Floating f => a f -> Maybe f
  put :: Floating f => 

data Average a = Average Word64 a
data Variance a ...

instance Aggregate Average where

instance Aggregate Variance where

instance Aggregate Quantile where

这里明显的问题是它new不是参数化的,因此Quantile不能用p参数初始化。添加参数new是可能的,但它会导致所有其他非参数构造函数忽略该值,这不是一个好的设计。

标签: haskelldesign-patternspattern-matchingtypeclass

解决方案


您缺少“codata”编码,这听起来可能最适合您的问题。

data Aggregate a = Aggregate 
    { get :: Maybe a
    , put :: a -> Aggregate a
    }

-- Use the closure to keep track of local state.
newAverage :: (Floating a) => Aggregate a
newAverage = Aggregate { get = Nothing, put = go 0 0 }
    where
    go n total x = Aggregate { get = Just ((total + x) / (n+1))
                             , put = go (n+1) (total+x)
                             }

-- Parameters are not a problem.
newQuantile :: (Floating a) => a -> Aggregate a
newQuantile p = Aggregate { get = ... ; put = \x -> ... }

...

出于某种原因,这种方法总是在具有 OO 背景的人的雷达下溜走,这很奇怪,因为它与该范式非常接近。


推荐阅读