根据枚举类型推广数据字段的正确方法

时间:2016-06-23 14:15:16

标签: haskell dependent-type

使用一个固定结构我们可以写

data Stats = Stats { lines :: !Int, words :: !Int }

instance Num Stats where
    fromInteger x = Stats x x
    (Stats a b) + (Stats a' b') = Stats (a + a') (b + b')

我们可以创建一些动态结构来实现通用版本

newtype Stats a = Stats { unStats :: [Int] } -- or Map, Vector, ...

instance forall a . (Enum a, Bounded a) => Num (Stats a) where
    fromInteger = Stats . replicate sz
                  where sz = fromEnum (maxBound::a) - fromEnum (minBound::a) + 1
    (Stats a) + (Stats b) = Stats $ zipWith (+) a b

(¨) :: forall a . (Eq a, Enum a, Bounded a) => Int -> a -> Stats a
x ¨ u = Stats $ map (\k -> if k == u then x else 0) [minBound .. maxBound :: a]

并用作

data TextStats = Lines | Words deriving (Eq, Ord, Enum, Bounded)

someTextStats :: Stats TextStats
someTextStats = 1 ¨Lines + 5 ¨Words

前一种方式是静态的(例如,单位测量函数将是),但后者不是在运行时应该遍历定义的结构。

除了Template Haskell之外还存在什么方式? THK!

1 个答案:

答案 0 :(得分:2)

如果您使用RankNTypesScopedVariables并且不尝试使用双引号作为运算符名称,则您的方法有效:

{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}

newtype Stats a = Stats { unStats :: [Integer] } -- or Map, Vector, ...
  deriving (Show)

instance forall a . (Enum a, Bounded a) => Num (Stats a) where
    fromInteger = Stats . replicate sz
                  where sz = fromEnum (maxBound::a) - fromEnum (minBound::a) + 1
    (Stats a) + (Stats b) = Stats $ zipWith (+) a b

(€) :: forall a . (Eq a, Enum a, Bounded a) => Integer -> a -> Stats a
x € u = Stats $ map (\k -> if k == u then x else 0) [minBound .. maxBound :: a]

data TextStats = Lines | Words deriving (Eq, Ord, Enum, Bounded)

test = 3 € Lines + 5 € Words

我还将列表更改为包含整数而不是Ints。这是你在找什么?