haskell - 基于右 Kan 扩展的列表

标签 haskell category-theory

在``Kan Extensions for Program Optimisation '' Ralf Hinze 的 List 类型的定义是基于从 monoids 的范畴沿自身的遗忘仿函数的右 Kan 扩展(第 7.4 节)。论文给出了 Haskell 的实现如下:

newtype List a = Abstr {
  apply :: forall z . (Monoid z) => (a -> z) -> z
  } 

我能够定义通常的 nil 和 cons 构造函数:
nil :: List a
nil = Abstr (\f -> mempty)

cons :: a -> List a -> List a
cons x (Abstr app) = Abstr (\f -> mappend (f x) (app f))

使用 Maybe 仿函数的 Monoid 类的以下实例,我设法定义了 head 函数:
instance Monoid (Maybe a) where
  mempty = Nothing
  mappend Nothing m = m
  mappend (Just a) m = Just a

head :: List a -> Maybe a
head (Abstr app) = app Just

问题 : 如何定义尾函数?

最佳答案

这是一次实现头部和尾部的相当原则的解决方案(full gist):

首先,我们知道如何追加列表(稍后会有用):

append :: List a -> List a -> List a
append (Abstr xs) (Abstr ys) = Abstr (\ f -> xs f <> ys f)

那么我们引入一个新类型Split我们将使用它来检测 List是否为空(如果它不为空,则获取头部和尾部):
newtype Split a = Split { outSplit :: Maybe (a, List a) }

这种新类型形成了一个幺半群:我们确实知道如何附加两个列表。
instance Monoid (Split a) where
  mempty = Split Nothing
  mappend (Split Nothing)        (Split nns)            = Split nns
  mappend (Split mms)            (Split Nothing)        = Split mms
  mappend (Split (Just (m, ms))) (Split (Just (n, ns))) =
    Split $ Just (m, append ms (cons n ns))

这意味着我们可以从 List a 获得一个函数至Split a使用 List aapply :
split :: List a -> Split a
split xs = apply xs $ \ a -> Split $ Just (a, nil)
headtail终于可以从 split 中简单地推导出来:
head :: List a -> Maybe a
head = fmap fst . outSplit . split

tail :: List a -> Maybe (List a)
tail = fmap snd . outSplit . split

关于haskell - 基于右 Kan 扩展的列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27381133/

相关文章:

haskell - haskell中给定种子的随机数质量

haskell - 评估部分应用功能

haskell - 有没有办法将一元减号(否定)重新绑定(bind)到与 Num 不同的类型类?

haskell - 类别中初始对象和终止对象之间的差异

haskell - 寻找与 liftA2 相关的 Haskell 函数,但其​​工作方式类似于 Alternative 中的 <|>

scala - 参数化类与函数

haskell - 门德勒的组织形态论

haskell - 为什么我对 signum 的定义类型正确?

haskell - Free 和 Cofree 的不动点仿函数

haskell - haskell中函数的双仿函数在哪里?