我试图将我的聚合器类型提升为一个类,但我无法弄清楚如何去做。
我有这个:
data Aggregator a b = Aggregator { aggregate :: [a] -> b }
minMax = Aggregator (\xs -> (minimum xs, maximum))
我想要像:
class Aggregator (g a b) where
aggregate :: g -> [a] -> b
所以我可以这样做:
data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b)
instance Aggregator (DoubleAggregator a b) where
aggregate (DoubleAggregator f g) as = (f as, g as)
这显然不起作用。我想我需要MultipleParamTypeClasses
或FamilyTypes
,但我无法弄明白。我知道,特别是我不需要上课,只能使用data Aggregator a b ...
,但我仍然对如何使用课程感兴趣。那我怎么能这样做呢?
答案 0 :(得分:4)
除了使用类型系列或功能依赖项(如在user2407038的答案中),另一种方法是使用GADT。
{-# LANGUAGE GADTs #-}
class Aggregator g where
aggregate :: g a b -> [a] -> b
data SingleAggregator a b = SingleAggregator ( [a] -> b )
data DoubleAggregator a b where
DoubleAggregator :: ([a] -> b) -> ([a] -> b) -> DoubleAggregator a (b, b)
instance Aggregator SingleAggregator where
aggregate (SingleAggregator f) as = f as
instance Aggregator DoubleAggregator where
aggregate (DoubleAggregator f g) as = (f as, g as)
这是一个更好还是更差的选择取决于你的确切用例,但我发现它在实践中比类型类扩展更简单。
如果你愿意,GADT方法也允许你完全抛弃类型类,只使用单一的代数类型。
data Aggregator a b where
SingleAggregator :: ([a] -> b) -> Aggregator a b
DoubleAggregator :: ([a] -> b) -> ([a] -> b) -> Aggregator a (b, b)
aggregate :: Aggregator a b -> [a] -> b
aggregate (SingleAggregator f) as = f as
aggregate (DoubleAggregator f g) as = (f as, g as)
然而,可能最接近这个的“Haskell-y”方法是只有一个Aggregator
新类型,并使其与Applicative
类型类可组合。例如:
import Control.Applicative
newtype Aggregator a b = Aggregator { aggregate :: [a] -> b }
instance Functor (Aggregator a) where
fmap f (Aggregator g) = Aggregator (f . g)
instance Applicative (Aggregator a) where
pure = Aggregator . const
Aggregator f <*> Aggregator x = Aggregator $ f <*> x
现在您可以定义简单的聚合器,例如
minAgg = Aggregator minimum
maxAgg = Aggregator maximum
然后使用Applicative
接口
minMax = liftA2 (,) minAgg maxAgg
答案 1 :(得分:2)
请注意,DoubleAggregator
的实例输入效果不好,因为输出为(b,b)
,但类声明表明它必须为b
。因此,您要么想出一种方法将两个b
合并为一个,这可能是不可取的,或者使您的输出类型取决于您的聚合器类型:
{-# LANGUAGE TypeFamilies #-}
class Aggregator g where
type Result x y z
aggregate :: g a b -> [a] -> Result g a b
data SingleAggregator a b = SingleAggregator ([a] -> b)
instance Aggregator SingleAggregator where
type Result SingleAggregator a b = b
aggregate (SingleAggregator f) = f
data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b)
instance Aggregator DoubleAggregator where
type Result DoubleAggregator a b = (b,b)
aggregate (DoubleAggregator f g) as = (f as, g as)
请注意,该类未提及a
或b
,因为它们都可以自由为任何类型,并且不依赖于聚合器类型。类型Result
是三个功能的函数:聚合器的类型和聚合器内的类型。结果可能永远不会取决于输入类型a
,在这种情况下,您可以编写如下内容:
class Aggregator g where
type Result x y
aggregate :: g a b -> [a] -> Result g b
....
type Result SingleAggregator b = b
....
type Result DoubleAggregator b = (b, b)
您也可以使用MultiParamTypeClasses
和FunctionalDependancies
执行此操作,但它更复杂且类型更难阅读,所以在我看来这是一个更糟糕的解决方案,我只是为了完整性而包含它
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
class Aggregator g a b r | g a b -> r where
aggregate :: g a b -> [a] -> r
data SingleAggregator a b = SingleAggregator ([a] -> b)
instance Aggregator SingleAggregator a b b where
aggregate (SingleAggregator f) = f
data DoubleAggregator a b = DoubleAggregator ([a] -> b) ([a] -> b)
instance Aggregator DoubleAggregator a b (b,b) where
aggregate (DoubleAggregator f g) as = (f as, g as)
主要区别在于功能依赖性:| g a b -> r
;这表示某些r
可以存在一个唯一的g a b
。