列表特有的阿朴素类型是什么?如何实现?

时间:2019-05-26 12:17:46

标签: haskell recursion-schemes unfold

我正在学习递归方案,事实证明对我来说,实现特定于列表类型的递归方案非常有用。但是,我被困在了阿朴性上。

这是我最近发现的载脂蛋白tails的实现:

import Data.Functor.Foldable

tailsApo :: [a] -> [[a]]
tailsApo = apo coalgTails
    where
    coalgTails = \case
        [] -> Cons [] (Left [])
        li@(_:xs) -> Cons li (Right xs)

不幸的是,我无法使用GHCi导入Data.Functor.Foldable,因为我收到未找到软件包的错误。另一个搜索显示了特定于列表的载脂蛋白的这种实现方式:

apoList :: ([b] -> Maybe (a, Either [b] [a])) -> [b] -> [a]
apoList f b = case f b of
    Nothing -> []
    Just (x, Left c)  -> x : apoL f c
    Just (x, Right e) -> x : e

很显然,apoList的第一个参数与tailsApo不匹配。我将类型扩展为类似apoList :: ([b] -> Either (a, b) [a])) -> [b] -> [a]

似乎没有关于此主题的新手入门信息。感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

The type is

apo :: (a ->           Base t   (Either t  a  ))      -- g :: a -> Base t r
    ->  a -> t 

apo  g  a =  rec a  where                             -- rec = apo g :: a -> t
             rec = embed . fmap (either id rec) . g  
{-
type family                           Base t :: * -> * 
embed                ::               Base t    t -> t
fmap (either id rec) :: Base t   r -> Base t    t
      either id rec  ::          r ->           t            r ~ Either t a
          g :: a ->     Base t   r                           r ~ Either t a
rec = apo g :: a ->                                  t
-}

a是种子。对于t ~ [b] we'll have

type instance Base [b] = ListF b
data                     ListF b r = Nil | Cons b r

Base t (Either t a) ~    ListF b (Either [b] a) 
                    ~                Maybe     (b, Either [b] a)

所以总的来说会

apoList :: (a -> Maybe (b, Either [b] a)) -> a -> [b] 
apoList coalg a = case coalg a of
   Nothing           -> []  -- (embed  Nil       )                       -- either
   Just (b, Left bs) -> b : bs   -- no more seed, no more steps to do!   --   id    $ bs
   Just (b, Right a) -> b : apoList coalg a  -- new seed, go on!         --   apo g $ a
                     -- ^^^^^  (embed (Cons b bs))

如此

apoTails :: [a] -> [[a]]      -- [[a]] ~ [b], b ~ [a]
apoTails = apoList tailsCoalg
  where
  -- tailsCoalg :: [a] -> Maybe ([a], Either [[a]] [a])
  tailsCoalg []       = Just ([], Left [])
  tailsCoalg s@(_:xs) = Just (s, Right xs)

答案 1 :(得分:2)

Data.Functor.Foldable recursion-schemes 软件包提供。 apo的类型为:

apo :: Corecursive t => (a -> Base t (Either t a)) -> a -> t 

在这里,t是展开生成的结构,而Base t是其展开函数。广义上讲,基函子表示递归结构的一个层次,其思想是,如果我们将其无限地嵌套在其内部,则我们将得到与整个结构等效的类型-实际上,这正是Fix来自Data.Functor.Foldable的信息。 (在元注释上,似乎在递归方案中没有专门针对Base的问答;拥有一个可能有用。)

Base的列表是:

data ListF a b = Nil | Cons a b

所以apo专用于:

apo @[_] :: (b -> ListF a (Either [a] b)) -> b -> [a]

如果我们想在不使用递归方案基础结构的情况下编写它,则可以使用ListF a bMaybe (a, b)同构的事实:

Nil     | Cons  a  b
Nothing | Just (a, b)

根据Maybe (a, b),签名将变为:

apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]

在余数中(即apo的函数参数),Nothing(或<{>递归方案版本的Nil)表示列表的生成应通过用空尾盖住列表来停止。这就是为什么即使您还使用Maybe通过其他方式使展开短路的原因,仍然需要Either的原因。

apoList的实现与您问题中的实现非常相似,不同之处在于此签名不将(b类型的种子)限制为列表,并且翻转了LeftRight(这样Left表示发生短路):

apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]
apoList f b = case f b of
    Nothing -> []
    Just (x, Left e) -> x : e
    Just (x, Right c) -> x : apoList f c