流媒体包的Stream数据类型是否等同于FreeT?

时间:2018-05-29 13:17:44

标签: haskell stream monad-transformers free-monad

streaming包定义了Stream类型,如下所示:

data Stream f m r
  = Step !(f (Stream f m r))
 | Effect (m (Stream f m r))
 | Return r

Stream类型的评论说明如下:

  

Stream数据类型相当于FreeT,可以代表任何有效的连续步骤,其中步骤的形式或命令'由第一个(仿函数)参数指定。

我想知道Stream类型如何等同于FreeT

以下是FreeT的定义:

data FreeF f a b = Pure a | Free (f b)
newtype FreeT f m a = FreeT { runFreeT :: m (FreeF f a (FreeT f m a)) }

看起来不可能在这两种类型之间创建同构。

具体来说,我没有办法编写以下两个使它们成为同构的函数:

freeTToStream :: FreeT f m a -> Stream f m a
streamToFreeT :: Stream f m a -> FreeT f m a

例如,我不确定如何将Return "hello" :: Stream f m String的值表示为FreeT

我想它可以像下面那样完成,但Pure "hello"必须包含在m中,而在Return "hello" :: Stream f m String则不是:

FreeT $ pure $ Pure "hello" :: Applicative m => FreeT f m a

Stream可以被视为等同于FreeT,即使它们似乎不可能在它们之间创建同构吗?

2 个答案:

答案 0 :(得分:4)

有一些小的差异使它们在字面上不等同。特别是,FreeT强制执行fm的替换,

FreeT f m a = m (Either a (f (FreeT f m a) = m (Either a (f (m (...))))
                                          -- m            f  m  -- alternating

虽然Stream允许口吃,例如,我们可以在两个Step之间构建以下内容,而Effect之间没有Effect (return (Effect (return (Return r))))

Return r

在某种意义上应该等同于

Stream

因此,我们将通过以下等式EffectEffect (m >>= \a -> return (Effect (k a))) = Effect (m >>= k) Effect (return x) = x 的{​​{1}}:

freeT_stream :: (Functor f, Monad m) => FreeT f m a -> Stream f m a
freeT_stream (FreeT m) = Effect (m >>= \case
  Pure r -> return (Return r)
  Free f -> return (Step (fmap freeT_stream f))

stream_freeT :: (Functor f, Monad m) => Stream f m a -> FreeT f m a
stream_freeT = FreeT . go where
  go = \case
    Step f -> return (Free (fmap stream_freeT f))
    Effect m -> m >>= go
    Return r -> return (Pure r)

在该商下,以下是同构

go

请注意Effect循环以展平多个(freeT_stream . stream_freeT) = id构造函数。

Pseudoproof:x

我们在流m上进行归纳。说实话,我是凭空推出诱导假设的。肯定存在感应不适用的情况。这取决于fm是什么,并且可能还有一些非常重要的设置以确保此方法对商类型有意义。但是仍然应该有许多具体的f(freeT_stream . stream_freeT) x = freeT_stream (FreeT (go x)) = Effect (go x >>= \case Pure r -> return (Return r) Free f -> return (Step (fmap freeT_stream f))) 这个方案适用。我希望有一些明确的解释可以将这种伪形式转化为有意义的东西。

x = Step f

案例fmap (freeT_stream . stream_freeT) f = f,归纳假设(IH)= Effect (return (Step (fmap freeT_stream (fmap stream_freeT f)))) = Effect (return (Step f)) -- by IH = Step f -- by quotient

x = Return r

案例= Effect (return (Return r)) = Return r -- by quotient

x = Effect m

案例m >>= (return . freeT_stream . stream_freeT)) = m,归纳假设= Effect ((m >>= go) >>= \case ...) = Effect (m >>= \x' -> go x' >>= \case ...) -- monad law = Effect (m >>= \x' -> return (Effect (go x' >>= \case ...))) -- by quotient = Effect (m >>= \x' -> (return . freeT_stream . stream_freeT) x') -- by the first two equations above in reverse = Effect m -- by IH

|--------+
|this is |
|a marker|
|--------+
|

将左转作为练习。

答案 1 :(得分:4)

Effect的示例和嵌套FreeT构造函数的示例都不能由f使用相同的参数mStream f m a来表示。还有更多反例。数据类型的根本差异可以在手动波动的空间中看到,其中数据构造函数被剥离并且允许无限类型。

FreeT f m aa都用于在fm类型构造函数中嵌套Stream类型。 f允许mFreeT的任意嵌套,而m更加严格。它总是有一个外f。它包含m和另一个a并重复,或Stream并终止。

但这并不意味着两种类型之间没有某种等价物。您可以通过显示每种类型可以忠实地嵌入到另一种类型中来显示一些等效性。

FreeT内嵌入f'可以在一个观察的背面完成:如果您选择m'fm并且f类型构造函数在每个级别都是可选的,您可以模拟mData.Functor.Sum的任意嵌套。一种快速的方法是使用streamToFreeT :: Stream f m a -> FreeT (Sum Identity f) (Sum Identity m) a streamToFreeT = undefined -- don't have a compiler nearby, not going to even try ,然后编写一个函数:

Sum Identity

请注意,该类型不具备必要的实例。这可以通过将Monad切换为实际具有适当FreeT实例的更直接类型来纠正。

向另一个方向转变并不需要任何改变类型的技巧。 Stream更受限制的形状已经可以直接嵌入R内。

我说这会使文档正确,但可能它应该使用比#34;等效的更精确的术语"。你可以使用一种类型构建的任何东西,你可以用另一种构造 - 但是可能会有一些额外的嵌入解释和涉及的变量的变化。