[英]what is the relationship between Haskell's FreeT and Coroutine type
[英]Is the streaming package's Stream data type equivalent to FreeT?
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 fm String
作為FreeT
。
我想它可以像下面那樣完成,但是Pure "hello"
必然會被包裝在m
,而在Return "hello" :: Stream fm String
它不是:
FreeT $ pure $ Pure "hello" :: Applicative m => FreeT f m a
可以認為Stream
等同於FreeT
即使它們之間似乎不可能創建同構嗎?
有一些小的差異使它們在字面上不等同。 特別是, 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)
注意go
循環以展平多個Effect
構造函數。
(freeT_stream . stream_freeT) = id
: (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)))
案例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
情況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
匡威作為練習。
使用Return
示例和具有嵌套Effect
構造函數的示例都不能由具有相同參數f
和m
FreeT
表示。 還有更多反例。 數據類型的潛在差異最好在手動波形空間中看到,其中數據構造函數被剝離並且允許無限類型。
兩個Stream fma
和FreeT fma
是嵌套的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
。
我說這會使文檔正確,但可能它應該使用比“等效”更精確的術語。 你可以使用一種類型構建的任何東西,你可以用另一種構造 - 但是可能會有一些額外的嵌入解釋和涉及的變量的變化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.