所以我们有免费的monad :(编码可能会有所不同,但是都一样)
data Free f a = Pure a
| Impure (f (Free f a))
instance Functor f => Monad (Free f) where
pure = Pure
Pure x >>= f = f x
Impure x >>= f = Impure ((>>= f) <$> x)
liftFree :: Functor f => f a -> Free f a
liftFree x = Impure (Pure <$> x)
runFree :: Monad g => (forall x. f x -> g x) -> Free f a -> g a
runFree _ (Pure x) = pure x
runFree f (Impure x) = join (runFree f <$> f x)
使得runFree
形成monad同态,这是自由monad的定义属性。
runFree f (pure x) = pure x
runFree f (liftFree x >>= liftFree . g) = f x >>= (f . g)
-- + some other associativity requirements
我们还可以与免费的Bind
from semigroupoids(我相信是这样)进行类似的构造,后者是仅绑定>>-
的函子:
data Free1 f a = Done (f a)
| More (f (Free1 f a))
instance Functor f => Bind (Free f) where
Done x >>- f = More (f <$> x)
More x >>- f = More ((>>- f) <$> x)
liftFree1 :: f a -> Free1 f a
liftFree1 = Done
runFree1 :: Bind g => (forall x. f x -> g x) -> Free1 f a -> g a
runFree1 f (Done x) = f x
runFree1 f (More x) = f x >>- runFree1 f
我们得到了适当的绑定同态:
使得runFree1
形成绑定同态,这是定义属性:
runFree1 f (liftFree1 x >>- liftFree1 . g) = f x >>- (f . g)
-- + some associativity laws
现在,这两种类型都很棒。我们可以将Free1
转换为Free
,这很有意义:
toFree :: Free1 f a -> Free f a
toFree (Done x) = Impure (Pure <$> x)
toFree (More x) = Impure (toFree <$> x)
,但是向后转换比较棘手。要从Free
到Free1
,我们必须处理两种情况:
Free
是纯净的,因此不能用Free1
表示。Free
不纯,因此可以用Free1
表示。这两种情况可以静态处理是有道理的,因为我们可以在Pure
或Impure
上进行匹配。
因此,合理的类型签名应该是:
fromFree :: Functor f => Free f a -> Either a (Free1 f a)
但是我在写这篇文章时遇到了问题。
fromFree :: Free f a -> Either a (Free1 f a)
fromFree (Pure x) = Left x -- easy
fromFree (Impure x) = Right ?? -- a bit harder
主要问题在于,我们需要确定是否使用Done
或More
构造函数,而无需“运行” f。我们需要一个:
f (Free f a) -> Free1 f a
听起来像函子可能麻烦,如果您无法“离开”,例如IO
。
所以,这听起来是不可能的,除非我缺少任何东西。
我尝试了另一种编码:
data Free1 f a = Free1 (f (Free f a))
此做让我们定义fromFree
,它是从NonEmpty
构造(data NonEmpty a = a :| [a]
)借用的。而且我在定义“免费Apply
”时可以使用这种方法,这很好。这确实使我们可以编写toFree
,fromFree
,liftFree1
和Bind
实例。但是,我似乎无法写runFree1
:
runFree1 :: Bind g => (forall x. f x -> g x) -> f (Free f a) -> g a
只要我做任何事情,我似乎都需要return :: a -> g a
,但是我们对所有Bind g
都没有这个要求(我找到了可能进行类型检查的版本,但是它会执行两次效果,并且所以不是适当的绑定同态)
因此,尽管此方法为我们提供了fromFree
,但我似乎找不到写runFree1
的方法,这正是赋予它“免费Bind
”功能的原因
在这两种方法中,我们要么:
Bind
拥有实际的免费runFree1
,但与Free
不兼容,因为您无法将Free
匹配成{{1 }}或纯值。Free1
兼容的类型(可能是“非空Free
”),但实际上不是自由的Free
(没有Bind
)并且失败整个目的。由此我可以得出以下两个结论之一:
runFree1
兼容的免费Free1
绑定,但是我还无法弄清楚Free
兼容的免费Bind
。这似乎与我的直觉相矛盾(我们总是可以立即确定Free
是纯净的还是不纯净的,因此我们也应该能够立即区分纯净(零影响)还是Free
),但是也许在那里是我更想念的更深层原因吗?是哪种情况?如果是#1,那是方法,如果是#2,更深层的原因是什么? :)
提前谢谢!
编辑为了消除我是否正在使用“真正的免费绑定”的不确定性,我开始研究一种真正意义上的免费绑定:
Free1
我似乎也无法为此写newtype Free1 f a = Free1 { runFree1 :: forall g. Bind g => (forall x. f x -> g x) -> g a }
。最后,我似乎需要一个fromFree
。
如果我不能为此写g (Either a (Free1 a)) -> g a
,那么就可以说我不能为自由绑定的任何实现写fromFree
,因为所有实现都与此同构。
有没有办法为此写fromFree
?还是全部不可能:'((对于fromFree
/ Alt
和Plus
/ Apply
来说都很好。
答案 0 :(得分:4)
虽然Free f a
是具有“ f
节点”和“ a
”叶的树的类型,但“自由绑定结构” Free1 f a
却是这种类型的树。有其他限制的树:f
节点的子节点要么全部是叶子,要么全部是f
节点。因此,如果我们考虑二叉树:
data Bin x = Bin x x
然后Free Bin a
包含以下树形,但Free1 Bin a
不包含:
Impure (Bin
(Pure a)
(Impure (Bin (Pure a) (Pure a))))
因为根节点有一个叶子和一个Bin
节点作为子节点,而Free1 Bin a
应该有两个叶子或两个Bin
节点。这样的模式可能会在Free
树的深处发生,因此仅凭Free f a -> Maybe (Free1 f a)
约束就不可能进行部分转换Functor f
。一个Traversable f
约束(假设遍历是有限的)使转换成为可能,但是对于大树当然当然不可行,因为在产生任何输出之前必须完全遍历它们。
请注意,由于Free1
的上述特征,因此根据Free
的其他定义实际上并不等效:
data Free1 f a = Free1 (f (Free f a)) -- Not a free Bind