首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Comonad Fix组合器

使用Comonad Fix组合器
EN

Stack Overflow用户
提问于 2017-07-18 14:19:51
回答 1查看 257关注 0票数 3

所以我最近一直在尝试固定点,并最终通过常规的固定点来发现一些用途;现在我正在转向共生固定点,我担心我被卡住了;

下面是我尝试过的几个例子,以及哪些成功了,哪些失败了:

代码语言:javascript
复制
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
module WFix where

import Control.Comonad
import Control.Comonad.Cofree
import Control.Monad.Fix

因此,我从loeb定理作为一个列表开始;列表中的每个元素都是一个函数,它接受最终结果来计算它的答案;这让我可以进行“电子表格”计算,其中值可以依赖于其他值。

代码语言:javascript
复制
spreadSheetFix :: [Int]
spreadSheetFix = fix $ \result -> [length result, (result !! 0) * 10, (result !! 1) + 1, sum (take 3 result)]

好了,我已经完成了基本的修复工作,是时候转向comonad类型了!下面是几个简单的comonads示例:

代码语言:javascript
复制
  data Stream a = S a (Stream a)
    deriving (Eq, Show, Functor)

  next :: Stream a -> Stream a
  next (S _ s) = s

  instance Comonad Stream where
    extract (S a _) = a
    duplicate s@(S _ r) = S s (duplicate r)

  instance ComonadApply Stream where
    (S f fs) <@> (S a as) = S (f a) (fs <@> as)

  data Tape a = Tape [a] a [a]
    deriving (Show, Eq, Functor)

  moveLeft, moveRight :: Tape a -> Tape a
  moveLeft w@(Tape [] _ _) = w
  moveLeft (Tape (l:ls) a rs) = Tape ls l (a:rs)

  moveRight w@(Tape _ _ []) = w
  moveRight (Tape ls a (r:rs)) = Tape (a:ls) r rs

  instance Comonad Tape where
    extract (Tape _ a _) = a
    duplicate w@(Tape l _ r) = Tape lefts w rights
      where
        lefts = zipWith const (tail $ iterate moveLeft w) l
        rights = zipWith const (tail $ iterate moveRight w) r

  instance ComonadApply Tape where
    Tape l f r <@> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')

好的,下面的组合子来自Control.Comonad

代码语言:javascript
复制
wfix :: Comonad w => w (w a -> a) -> a
wfix w = extract w (extend wfix w)

cfix :: Comonad w => (w a -> a) -> w a
cfix f = fix (extend f)

kfix :: ComonadApply w => w (w a -> a) -> w a
kfix w = fix $ \u -> w <@> duplicate u

我从尝试wfix开始:

代码语言:javascript
复制
streamWFix :: Int
streamWFix = wfix st
  where
    incNext = succ . extract . next
    st = (S incNext (S incNext (S (const 0) st)))

> streamWFix
-- 2

这似乎是通过调用w上的第一个w a -> a来工作的,直到在本例中达到解析const 0为止;这是有意义的。我们也可以使用磁带来完成此操作:

代码语言:javascript
复制
selfReferentialWFix :: Int
selfReferentialWFix = wfix $ Tape [const 10] ((+5) . extract . moveLeft) []
-- selfReferentialWFix == 15

我想我明白了,但是下一次我有点卡住了,我似乎没有直觉知道cfix应该做什么。即使是最简单的东西,当我评估它时,我也会永远想着旋转;甚至尝试使用getOne提取流的第一个元素也失败了。

代码语言:javascript
复制
getOne :: Stream a -> a
getOne (S a _) = a

simpleCFix :: Stream Int
simpleCFix = cfix go
  where
    go _ = 0

与kfix类似;即使是简单的尝试似乎也不会终止。我对kfix的理解是,每个“槽”中的函数都会传递一个专注于该点的已评估comonad的副本;是这样吗?

我试着在这上面使用'getOne‘:

代码语言:javascript
复制
streamKFix :: Stream Int
streamKFix = kfix st
  where
    go _ = 0
    st = S go st

下面是使用Tape的有限尝试,它也无法运行:

代码语言:javascript
复制
tapeKFix :: Tape Int
tapeKFix = kfix $ Tape [] (const 0) []

那么,对于我的问题,有人能提供一些使用cfix和kfix的可运行的(非平凡的)示例,并解释它们是如何工作的吗?我计划使用kfix来最终做一个“Conway的生活游戏”风格的实验,我认为kfix在与给定细胞周围的邻居一起工作时是有用的,对吗?

请随时提出任何澄清的问题,并帮助我扩展我的知识和直觉的fix!

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-07-18 16:27:23

TapeComonadApplyComonad实例还不够懒惰,不能与kfix一起使用。

适用于Tapeduplicate要求您先证明磁带存在,然后才能断定结果为Tape

代码语言:javascript
复制
instance Comonad Tape where
  extract (Tape _ a _) = a
  duplicate w@(Tape l _ r) = Tape lefts w rights
--             ^             ^
-- matches a Tape            |               
-- before determining that the result is a Tape

<@>会先检查两个参数是否都是磁带,然后才能得出结果是Tape

代码语言:javascript
复制
instance ComonadApply Tape where
  Tape l f r <@> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')
-- ^             ^              ^
-- matches two Tapes            |
-- before detrmining that the result is a Tape

总而言之,kfix (Tape _ _ _)永远不可能生成Tape

代码语言:javascript
复制
kfix w            = fix $ \u -> w            <@> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <@> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <@> case u of (Tape _ _ _) -> ...
--                         ^                                |
--                         ----------- <<loop>> -------------

您可以通过使用irrefutable patterns提高duplicate和/或<@>的工作效率来解决此问题。即使还没有生成Tape构造函数,模式~(Tape l a r)也是匹配的。下面是如何使用它来提高duplicate的工作效率

代码语言:javascript
复制
instance Comonad Tape where
  extract (Tape _ a _) = a
  duplicate w@(~(Tape l _ r)) = Tape lefts w rights
    where
      lefts = zipWith const (tail $ iterate moveLeft w) l
      rights = zipWith const (tail $ iterate moveRight w) r

不可反驳的模式匹配等同于使用函数来提取值。对于duplicate来说,它等同于编写

代码语言:javascript
复制
left  (Tape l _ _) = l
right (Tape _ _ r) = r

instance Comonad Tape where
  extract (Tape _ a _) = a
  duplicate w = Tape lefts w rights
    where
      l = left w
      r = right w
      ...
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45158934

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档