用于Vector.Stream

时间:2018-07-02 08:12:42

标签: haskell stream monads

Data.Vector.Stream提供了一个不错的Stream实现,这要归功于对可熔性的关注(有关更多信息,请参见this paper)。自vector-0.1以来,此Stream实现通过将Step类型移动到monad进行了少许更改。 (现在,该实现位于Data.Vector.Fusion.Stream.Monadic中。)

简而言之,这是Stream的定义:

data Step s a where
    Yield :: a -> s -> Step s a
    Skip  :: s -> Step s a
    Done  :: Step s a

data Stream a = forall s. Stream (s -> Step s a) s

Step s a封装了状态s的一次迭代和更新函数s -> Step s a的三种可能的结果。流是Done,或跳过输出,或产生输出。 (上面的定义使用了GADT,但这并不相关。)

Stream的简单应用是:

empty :: Stream a
empty = Stream (const Done) ()

singleton :: a -> Stream a
singleton x = Stream step True where
    step True  = Yield x False
    step False = Done

fromList :: [a] -> Stream a
fromList zs = Stream step zs
where
    step (x:xs) = Yield x xs
    step []     = Done

严格的左折是这样的:

foldl'S :: (a -> b -> a) -> a -> Stream b -> a
foldl'S f a0 (Stream step s) = go a0 s where
    go a s = a `seq`
                case step s of
                    Yield b s' -> go (f a b) s'
                    Skip    s' -> go a       s'
                    Done       -> a

并提供了通常的类似列表的功能lengthS = foldl'S (\n _ -> n+1) 0等。这当然不及ConduitPipes那样优雅,但它简单快捷。到目前为止一切顺利。

现在,让我们尝试将“低级”流聚合为更高级的流。例如,如果您有一个比特流Stream Bool,则可能需要使用一些聪明的编解码器对这些比特进行解码以产生Stream Int。当然,总是有可能从给定的s -> Step s b中提取的step函数中构建一个新的步进函数Stream step sstep :: s->Step s a函数的重复应用导致尴尬的case (step s) of ...级联,它们一遍又一遍地处理DoneSkipYield的三种可能性。理想情况下,aggregate应该这样:

aggregate :: Stream a -> M?? -> Stream b
newStream = aggregate oldStream $ do
    a1 <- get    -- a1 :: a
    if a1 == True then doSomething
    else do
        a2 <- get
        -- etc.

M??是要定义的单子。让我们尝试类型Appl s a

newtype Appl s a = Appl ((s->Step s a) -> s -> Step s a)

之所以称为Appl,是因为它具有功能应用程序的签名。 monad实例非常简单:

instance Monad (Appl s) where
    return a = Appl (\_ s -> Yield a s)
    (Appl ap) >>= f = Appl (\step s ->
        case (ap step s) of
            Done -> Done
            Skip s' -> untilNotSkip step s'
            Yield a' s' -> ap' step s' where Appl ap' = f a'

其中untilNotSkip :: (s->Step s a) -> s -> Step s a只是重复(嵌套)应用step :: (s->Step s a),直到返回DoneYield

get函数只是普通函数应用程序

get :: Appl s a
get = Appl(\step s -> step s)

要束手无策,需要完成FunctorApplicative,这是一个问题:Appl s不能用作函子。签名是

fmap :: (a->b) -> Appl s a -> Appl s b

那是行不通的,因为要从函数(s->Step s b) -> s -> Step s b)中创建函数(s->Step s a) -> s -> Step s a),我需要一个b->a。我可以在Appl s b上拉回a->b,但不能向前推Appl s a-也就是说,我可以有一个逆变函子,但不能有函子。那真是怪了。流are quite naturally comonads,但看不到连接。 Appl的目的是将步进功能s->Step s a变成另一个s->Step s b

这里有些错误,Appl不是正确的“ M??”。有人可以帮忙吗?

更新

夏莉瑶指出,类型应为

data Walk a b = forall s. Walk ((s->Step s a) -> s -> Step s b)

Functor,Applicative和Monad实例为

instance Functor (Step s) where
    fmap f Done        = Done
    fmap f (Skip s)    = Skip s
    fmap f (Yield a s) = Yield (f a) s

instance Functor (Walk a) where
    fmap f (Walk t) = Walk ( \step s -> fmap f (t step s) )

-- default Applicative given a monad instance
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap mf m = do
    f <- mf
    x <- m
    return (f x)

untilNotSkip :: (s->Step s a) -> s -> Step s a
untilNotSkip step s = case step s of
    Done        -> Done
    Skip s'     -> untilNotSkip step s'
    Yield a' s' -> Yield a' s'

instance Monad (Walk a) where
    return a = Walk (\_ s -> Yield a s)
    Walk t >>= f =
        Walk (\step s -> case t (untilNotSkip step) s of
            Done        -> Done
            Skip _      -> error "Internal error."
            Yield b' s' -> case f b' of Walk t' -> t' step s'   -- bad
    )

instance Applicative (Walk a) where
    pure = return
    (<*>) = ap

但是,类型检查器不允许此monad实例。在>>=的定义中,s中的Walk (\step s -> ...s'中的Yield b' s' -> ...不同,但是必须相同。这里的根本问题是(>>=) :: Walk a b -> (b->Walk a c) -> Walk a c具有两个独立的全量化状态s,一个在第一个参数中,另一个由b->Walk a c返回。实际上,这是(滥用符号) (forall s. Walk s a b) -> (forall s'. b->Walk s' a' c) -> (forall s''. Walk s'' a c),从概念上和类型检查器上都没有意义。三个ss's''必须是同一类型。

一种变体,其中Walk不能比s完全量化:

data Walk s a b = Walk ((s->Step s a) -> s -> Step s b)

允许正确定义绑定,但是aggregate无效:

-- does not compile
aggregate :: Stream a -> Walk s a b -> Stream b
aggregate (Stream step s) (M t) = Stream (t step) s

同样,流状态s必须始终相同。一种解决方法是引入data PreStream s a = PreStream (s -> Step s a) s,但也不允许aggregate :: Stream a -> ?? -> Stream b

源代码位于github上。

1 个答案:

答案 0 :(得分:2)

让我们再看一下.option-selected:hover { border: 3px solid #529c56 !important;cursor: default;},因为它似乎几乎是正确的。

Appl

该想法是通过将“低级”阶跃函数转换为“高阶”阶跃函数来定义流转换器。在这种情况下,这两个步骤函数不应​​具有相同的输出。例如,如果要将位转换为字节,则需要newtype Appl s a = Appl ((s->Step s a) -> s -> Step s a)

因此,更好的类型是

(s -> Step s Bit) -> s -> Step s Byte

此外,由于newtype Walk s b a = Walk ((s -> Step s b) -> s -> Step s a) -- A walk is many steps. Stream上存在量化,因​​此在某个时候您需要在s上进行一些通用量化才能使用s,因此您最好将它在类型定义中。

Walk