Haskell 单子的自由单子

Haskell 单子的自由单子,haskell,free-monad,Haskell,Free Monad,x>>=f是否等同于retract(liftF x>>=liftF.f) 也就是说,从同为单子的函子生成的自由单子的单子实例是否会有一个与原始单子等价的单子实例?我不知道您对retract的定义是什么,但给出了 retract :: Monad m => Free m a -> m a retract (Pure a) = return a retract (Impure fx) = fx >>= retract 及 注意(证据可能是错误的,是手工做的,没有检查) 你有

x>>=f
是否等同于
retract(liftF x>>=liftF.f)


也就是说,从同为单子的函子生成的自由单子的单子实例是否会有一个与原始单子等价的单子实例?

我不知道您对
retract
的定义是什么,但给出了

retract :: Monad m => Free m a -> m a
retract (Pure a) = return a
retract (Impure fx) = fx >>= retract

注意(证据可能是错误的,是手工做的,没有检查)

你有吗

retract (liftF x >>= liftF . f)
= retract ((Impure (fmap Pure x)) >>= liftF . f)
= retract $ Impure $ fmap (>>= liftF . f) $ fmap Pure x
= (fmap (>>= liftF . f) $ fmap Pure x) >>= retract
= (fmap (\y -> Pure y >>= liftF . f) x) >>= retract
= (fmap (liftF . f) x) >>= retract
= (liftM (liftF . f) x) >>= retract
= (x >>= return . liftF . f) >>= retract
= x >>= (\y -> (return $ liftF $ f y >>=  retract)
= x >>= (\y -> retract $ liftF $ f y)
= x >>= (\y -> retract . liftF $ f y)
= x >>= (\y -> f y)
= x >>= f
这并不意味着
freema
ma
同构,只是
retract
确实见证了一次撤回。请注意,
liftF
不是单子态射(
return
不转到
return
)。Free是functor类中的functor,但它不是monad类中的monad(尽管
retract
看起来很像
join
liftF
看起来很像
return

编辑:请注意,收回意味着某种等价性。定义

 ~ : Free m a -> Free m a -> Prop
 a ~ b = (retract a) ==_(m a) (retract b)

然后考虑商类型<代码>免费M A/~< /代码>。我断言此类型同构于

ma
。由于
(liftF(retract x))~x
因为
(retract.liftF.retract$x)==(uma)retract x
。因此,单子上的自由单子就是单子加上一些额外的数据。这与当
m
m
是幺半群时,
[m]
m
是“本质相同”的说法完全相同

也就是说,从函子生成的自由单子的单子实例(也是单子)是否会有一个与原始单子等价的单子实例

不,任何函子上的自由单子都是单子。因此,当Monad实例存在时,它无法神奇地知道它。它也不能“猜测”它,因为同一个函子可能以不同的方式成为单子(例如,不同的幺半群的编写器单子)

另一个原因是,询问这两个单子是否有等价的实例没有多大意义,因为它们甚至不是同构的类型。例如,考虑作者单子上的自由单子。它将是一个类似列表的结构。这两个例子相等意味着什么

不同monad实例的示例 如果上面的描述不清楚,下面是一个包含许多可能的Monad实例的类型示例

data M a = M Integer a

bindUsing :: (Integer -> Integer -> Integer) -> M a -> (a -> M b) -> M b
bindUsing f (M n a) k =
  let M m b = k a
  in M (f m n) b

-- Any of the below instances is a valid Monad instance
instance Monad M where
  return x = M 0 x
  (>>=) = bindUsing (+)

instance Monad M where
  return x = M 1 x
  (>>=) = bindUsing (*)

如果你把不纯净(a,mzero)视为等同于
a
,你会发现它们是一样的——你可能大多数时候都应该如此。你是说
mempty
而不是
mzero
?是的,考虑到
不纯(a,mzero)=a
自由(Writer m)是微不足道的,a同构于
Writer ma
Free(Writer m)a=Writer[m]a
,如果您关心的是单像结构,那么它们看起来很相似。它们的不同之处在于你可以编写一个函数来区分它们,另一方面,它们的不同之处在于你可能不应该:)。我并不反对。这就是为什么它是一个收缩,而不是一个同构。大多数时候,你真的想考虑<代码>免费Ma<代码>与“代码> M A<代码>和<代码> [M] < /代码>为“相同”,如<代码> M< /代码>。这可以很容易地用商类型(我们只能假装存在于Haskell中)形式化,并且是做一些事情的基础,比如使用自由单子实现高效的
管道
,即使该实现不遵守变换法则(因为
lift
不是单子态射)即使在Haskell中没有使这个“安全”的商。@RomanCheplyaka这很好,但是
retract
ion仍然使用底层的
Monad
实例
Free
monad应该总是比函子上定义的任何其他monad都大。缩回应该是唯一的映射,它使
自由
通用,不是吗?很好的答案,这确实探索了等价的本质,并暗示了用同一个函子生成两个不同单体的方法上的限制条件“注意,liftF不是单体态射(return不去return)”但是return确实在
收回
下返回。所以它们不是同构的,因为你可以区分它们,但在缩回模式下它们又变得无法区分了吗?@singpolyma确实如此。如果你在
retract
之后用相等的东西进行商运算,它们是同构的。否则,这只是一个收回。您想要的解释取决于您正在做什么,但对于某些应用程序,您确实希望在某些函子上使用
Free
,但需要一个monad转换器。得到这样一个变压器的明显方法是考虑<代码>自由(f:+:m),但这不是一个转换器,因为<代码> LIFTF < /代码>等不是单态射。一个很好的例子是
管道
。您可以区分它们,因此如果您想这样做,请将其包装在模块中/不要导出构造函数。
 ~ : Free m a -> Free m a -> Prop
 a ~ b = (retract a) ==_(m a) (retract b)
data M a = M Integer a

bindUsing :: (Integer -> Integer -> Integer) -> M a -> (a -> M b) -> M b
bindUsing f (M n a) k =
  let M m b = k a
  in M (f m n) b

-- Any of the below instances is a valid Monad instance
instance Monad M where
  return x = M 0 x
  (>>=) = bindUsing (+)

instance Monad M where
  return x = M 1 x
  (>>=) = bindUsing (*)