通用容器转换?如果从可折叠到替代?

时间:2018-02-08 08:14:38

标签: haskell containers applicative foldable

instance Alternative [](<|>) = (++)。所以我认为(<|>)是某种拼接器,导致看似几乎是通用的容器转换器:

-- (<|>) = generalization of (++)

(<|) :: Alternative f => x -> f x -> f x
x <| xs = pure x <|> xs

conv :: (Foldable t, Alternative f) => t x -> f x
conv = foldr (<|) empty

确实,我能够概括来自Data.List所有函数,这里有一些:

-- fmap = generalization of map

reverse :: (Foldable t, Alternative f) => t a -> f a
reverse = getAlt . getDual . foldMap (Dual . Alt . pure)

-- asum = generalization of concat

asumMap :: (Foldable t, Alternative f) => (a -> f b) -> t a -> f b -- generalization of concatMap
asumMap f = getAlt . foldMap (Alt . f)

splitAt :: (Foldable t, Alternative f, Alternative g) => Int -> t a -> (f a, g a)
splitAt n xs = let (_,fs,gs) = foldl' (\(i,z1,z2) x -> if 0 < i then (i-1,z1 . (x <|),z2) else (0,z1,z2 . (x <|))) (n,id,id) xs in (fs empty,gs empty)

此外,这个类比会产生一些有趣的新实例,例如用于函子和(Data.Functor.Sum)的工作应用函子:

instance (Foldable f, Applicative f, Alternative g) => Applicative (Sum f g) where
    pure = InL . pure
    InL f <*> InL x = InL (f <*> x)
    InL f <*> InR x = InR (conv f <*> x)
    InR f <*> InL x = InR (f <*> conv x)
    InR f <*> InR x = InR (f <*> x)

instance (Foldable f, Applicative f, Alternative g) => Alternative (Sum f g) where
    empty = InR empty
    InL x <|> _ = InL x
    InR _ <|> InL y = InL y
    InR x <|> InR y = InR (x <|> y)

通常使用所有函数进行概括,并使用此类比法创建新实例,尤其是列表操作?

编辑:我特别关注模棱两可的回归类型。对于常规列表操作,返回类型可从其参数类型中进行推导。但是&#34;通用&#34;版本不是,因为必须显式指定返回类型。这个问题严重到足以使这个比喻变得危险吗? (或者还有其他问题吗?)

编辑2:如果我完全理解foldl'的行为,则通用splitAt(如上所示)的时间复杂度必须为Θ(length xs),{{1}对每个元素都严格,对吧?如果是,那一定是个问题,因为它不如普通版foldl'

1 个答案:

答案 0 :(得分:3)

将函数设置为理论上可能的多态并不总是一个好主意,尤其不是函数参数。根据经验:将函数结果尽可能多态化。 (通常,参数将包含一些在结果中使用的类型变量。)只有在您有特定原因的情况下,还要赋予参数额外的多态性。

原因是:如果所有都是多态的,编译器没有关于选择哪些具体类型的提示。多态结果/值通常是正确的,因为它们通常直接或间接绑定到具有显式签名的某个顶级定义,但多态参数通常只用文字填充(数字文字是Haskell中的多态,并且字符串/列表也可以是)或其他多态值,因此您最终必须键入大量显式本地签名,这往往比偶尔抛出显式转换函数更尴尬,因为有些东西是不够多态

Foldable->Alternative的这个想法明确地存在另一个问题,Alternative类相当不受欢迎,没有非常坚实的数学支持。它基本上是应用函子的类,对于每个实例化都会产生Monoid。嗯,这也可以通过要求Monoid本身直接表达。因此,“通用容器转换功能”已经存在,它是foldMap pure