使用一个固定结构我们可以写
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!
答案 0 :(得分:2)
如果您使用RankNTypes
,ScopedVariables
并且不尝试使用双引号作为运算符名称,则您的方法有效:
{-# 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。这是你在找什么?