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
即使看起来不可能在FreeT
之间创建同构,Stream
也可以被认为等同于FreeT
吗?
发布于 2018-05-30 00:19:48
有一些小的差异使得它们在字面上不是等价的。特别是,FreeT
强制执行f
和m
的交替,
FreeT f m a = m (Either a (f (FreeT f m a) = m (Either a (f (m (...))))
-- m f m -- alternating
尽管Stream
允许断断续续,例如,我们可以在两个Effect
之间没有Step
的情况下构造以下代码
Effect (return (Effect (return (Return r))))
在某种意义上,它应该等同于
Return r
因此,我们将通过以下方程求出Stream
的商,这些方程使Effect
层变平
Effect (m >>= \a -> return (Effect (k a))) = Effect (m >>= k)
Effect (return x) = x
在该商下,以下是同构
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)
请注意用于扁平化多个Effect
构造函数的go
循环。
防伪:(freeT_stream . stream_freeT) = id
我们在流x
上进行归纳。老实说,我是凭空提出归纳假设的。当然有一些情况下,归纳是不适用的。这取决于m
和f
是什么,可能还需要一些重要的设置来确保这种方法对于商类型是有意义的。但是仍然应该有许多具体的m
和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)))
Case x = Step f
,归纳假说(IH) fmap (freeT_stream . stream_freeT) f = f
= 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
Case 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
向左对话作为练习。
发布于 2018-05-30 01:01:38
您的Return
示例和我的嵌套Effect
构造函数示例都不能用具有相同参数f
和m
的FreeT
表示。还有更多的反例。数据类型之间的潜在差异最能在手写空间中看到,在这个空间中,数据构造函数被剥离,并且允许无限类型。
Stream f m a
和FreeT f m a
都用于将a
类型嵌套在一堆f
和m
类型构造函数中。Stream
允许任意嵌套f
和m
,而FreeT
则更加严格。它总是有一个外部m
。它包含一个f
和另一个m
并重复,或者包含一个a
并终止。
但这并不意味着这两种类型之间没有某种等价性。您可以通过证明每种类型都可以忠实地嵌入到另一种类型中来显示某种等价性。
在FreeT
中嵌入Stream
可以在一个观察的基础上完成:如果您选择一个f'
和m'
,使得f
和m
类型的构造函数在每一层都是可选的,那么您可以对f
和m
的任意嵌套进行建模。一种快速的方法是使用Data.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
中。
我要说的是,这使得文档是正确的,尽管它可能应该使用比“等效”更精确的术语。可以用一种类型构造的任何东西,都可以用另一种类型构造--但可能会对嵌入进行一些额外的解释,并涉及到变量的更改。
https://stackoverflow.com/questions/50585417
复制相似问题