Haskell 具有类型依赖关系的多参数类
我正试图将我的聚合器类型升级到一个类,但我不知道该怎么做 我有这个:Haskell 具有类型依赖关系的多参数类,haskell,Haskell,我正试图将我的聚合器类型升级到一个类,但我不知道该怎么做 我有这个: 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 DoubleAg
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
,但我想不出来。我知道,在这一点上,我并不真正需要类,只需要数据聚合器ab…
,但我仍然对如何使用类感兴趣。那么我该怎么做呢?请注意,DoubleAggregator
的实例类型不好,因为输出是(b,b)
,但类声明说它必须是b
。因此,您要么想办法将两个b
s合并为一个,这可能是不可取的,要么让输出类型取决于聚合器类型:
{-# 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)
您也可以使用multiparamtypeclass
和functionaldependences
来实现这一点,但它更复杂,类型更难阅读,因此在我看来,这是一个更糟糕的解决方案,我只考虑完整性
{-# 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)
主要区别在于功能依赖性:
|gab->r
;也就是说,对于某些gab
,存在一个唯一的r
注意,DoubleAggregator
的实例类型不好,因为输出是(b,b)
,但是类声明说它必须是b
。因此,您要么想办法将两个b
s合并为一个,这可能是不可取的,要么让输出类型取决于聚合器类型:
{-# 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)
您也可以使用multiparamtypeclass
和functionaldependences
来实现这一点,但它更复杂,类型更难阅读,因此在我看来,这是一个更糟糕的解决方案,我只考虑完整性
{-# 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)
主要区别在于功能依赖性:
|gab->r
;也就是说,对于某些gab
,存在一个唯一的r
除了使用类型族或函数依赖项(如user2407038的回答中所示),另一种选择是使用
这是一个更好还是更差的选择取决于您的具体用例,但我发现它在实践中比类型类扩展简单得多
如果需要,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”的方法可能是只使用一个聚合器
newtype,并使其与Applicative
type类组合。例如:
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
然后使用Applicative
界面将它们组合成更复杂的聚合器
minMax = liftA2 (,) minAgg maxAgg
除了使用类型族或函数依赖项(如user2407038的回答中所示),另一种选择是使用 这是一个更好还是更差的选择取决于您的具体用例,但我发现它在实践中比类型类扩展简单得多 如果需要,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”的方法可能是只使用一个聚合器
newtype,并使其与Applicative
type类组合。例如:
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
然后使用Applicative
界面将它们组合成更复杂的聚合器
minMax = liftA2 (,) minAgg maxAgg
我知道您特别有兴趣用类处理这个问题,但这听起来更像是一个
折叠问题,即使您只是使用数据聚合器AB代码>,看起来空列表不太可能正确处理。您可能想研究幺半群和折叠?我知道您特别有兴趣用类来处理这个问题,但这听起来更像是折叠问题,即使您只是使用数据聚合器AB代码>,看起来空列表不太可能正确处理。你可能想看看幺半群和折叠?我真的很喜欢!谢谢(我可能终于明白了GADT的用途了。)。谢谢我还添加了一个使用应用程序的示例,我认为这是此类问题中最常用的模式。我真的很喜欢!谢谢(我可能终于明白了GADT的用途了。)。谢谢我还添加了一个使用应用程序的示例,我认为这是此类问题中最常用的模式。