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
,即使它们似乎不可能在它们之间创建同构吗?
答案 0 :(得分:4)
有一些小的差异使它们在字面上不等同。特别是,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
允许口吃,例如,我们可以在两个Step
之间构建以下内容,而Effect
之间没有Effect (return (Effect (return (Return r))))
:
Return r
在某种意义上应该等同于
Stream
因此,我们将通过以下等式Effect
取Effect (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
上进行归纳。说实话,我是凭空推出诱导假设的。肯定存在感应不适用的情况。这取决于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)))
这个方案适用。我希望有一些明确的解释可以将这种伪形式转化为有意义的东西。
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
使用相同的参数m
和Stream f m a
来表示。还有更多反例。数据类型的根本差异可以在手动波动的空间中看到,其中数据构造函数被剥离并且允许无限类型。
FreeT f m a
和a
都用于在f
和m
类型构造函数中嵌套Stream
类型。 f
允许m
和FreeT
的任意嵌套,而m
更加严格。它总是有一个外f
。它包含m
和另一个a
并重复,或Stream
并终止。
但这并不意味着两种类型之间没有某种等价物。您可以通过显示每种类型可以忠实地嵌入到另一种类型中来显示一些等效性。
在FreeT
内嵌入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
更受限制的形状已经可以直接嵌入R
内。
我说这会使文档正确,但可能它应该使用比#34;等效的更精确的术语"。你可以使用一种类型构建的任何东西,你可以用另一种构造 - 但是可能会有一些额外的嵌入解释和涉及的变量的变化。