The documentation for Free
says:
A number of common monads arise as free monads,
- Given
data Empty a
, Free Empty
is isomorphic to the Identity
monad.
- Free
Maybe
can be used to model a partiality monad where each layer represents running the computation for a while longer.
What other monads are expressible using Free
?
I could think only of one more: I believe Free (Const e)
is isomorphic to Either e
.
Edit: What monads are not expressible using Free
and why?
几乎所有的人(最多涉及循环和问题mfix
),但不Cont
。
考虑State
单子
newtype State s a = State (s -> (a,s))
看起来并不像一个免费的单子什么 ...但想想State
来讲你如何使用它
get :: m s --or equivalently (s -> m a) -> m a
set :: s -> m () --or (s,m a) -> m a
runState :: m a -> s -> (a,s)
我们可以通过列出的操作作为构造设计与此接口的自由单子
data StateF s a
= Get (s -> a) | Set s a deriving Functor
那么我们有
type State s a = Free (StateF s) a
同
get = Impure (Get Pure)
set x = Impure (Set x (Pure ())
我们只是需要一种方法来使用它
runState (Pure a) s = (a,s)
runState (Impure (Get f)) s = runState (f s) s
runState (Impure (Set s next)) _ = runState next s
你可以这样做建设与大多数的单子。 就像也许/偏袒单子被定义
stop :: m a
maybe :: b -> (a -> b) -> m a -> b
规则是,我们对待每一个在结束功能mx
一些x
作为仿函数构造,以及其他功能都运行产生的游离单子的方式。 在这种情况下
data StopF a = StopF deriving Functor
maybe _ f (Pure a) = f a
maybe b _ (Impure Stop) = b
这是为什么很酷吧? 那么几件事情
- 免费的单子给你一块,你能想到的作为对于一元代码的AST 数据 。 你可以写,在这个数据操作功能,这是DSL的真正有用的
- 函子组成,这意味着打破你的单子像这样使他们半composeable。 特别是,鉴于其共享代数2函子(一个代数本质上只是一个功能
fa -> a
对某些a
时f
是一个仿函数),该组合物还具有代数。
函子组成仅仅是我们可以在几个方面,其中大部分保留该代数结合仿函数。 在这种情况下,我们希望不函子的组合物 (f (g (x)))
但算符副产品 。 函子加
data f :+: g a = Inl (f a) | Inr (g a)
instance (Functor f, Functor g) => Functor (f :+: g) where
fmap f (Inl x) = Inl (fmap f x)
fmap f (Inr x) = Inr (fmap f x)
compAlg :: (f a -> a) -> (g a -> a) -> f :+: g a -> a
compAlg f _ (Inl x) = f x
compAlf _ g (Inr x) = g x
还免费单子保留代数
freeAlg :: (f a -> a) -> Free f a -> a
freeAlg _ (Pure a) = a
freeAlg f (Impure x) = f $ fmap (freeAlg f) x
在沃特Swierstra的著名论文数据类型点菜这是用来很大的影响。 从纸的一个简单例子是计算器。 我们将采取新的一个单子拿这个职位。 鉴于代数
class Calculator f where
eval :: f Integer -> Integer
我们能想到的各种实例
data Mult a = Mult a a deriving Functor
instance Calculator Mult where
eval (Mult a b) = a*b
data Add a = Add a a deriving Functor
instance Calculator Add where
eval (Add a b) = a+b
data Neg a = Neg a deriving Functor
instance Calculator Neg where
eval (Neg a) = negate a
instance Calculator (Const Integer) where
eval (Const a) = a
data Signum a = Signum a deriving Functor
instance Calculator Signum where
eval (Signum a) = signum a
data Abs a = Abs a deriving Functor
instance Calculator Abs where
eval (Abs a) = abs a
而最重要的
instance (Calculator f, Calculator g) => Calculator (f :+: g) where
eval = compAlg eval
您可以定义数字单子
newtype Numerical a = Numerical (
Free (Mult
:+: Add
:+: Neg
:+: Const Integer
:+: Signum
:+: Abs) a deriving (Functor, Monad)
然后你可以定义
instance Num (Numerical a)
这可能是完全无用的,但我觉得非常酷。 它不会让你定义其他像
class Pretty f where
pretty :: f String -> String
instance Pretty Mult where
pretty (Mult a b) = a ++ "*" ++ b
和类似的对他们的所有的休息。
这是一个有用的设计stategy:列出你希望你的单子做==>定义函子每个操作的东西==>搞清楚什么一些代数的应该是==>定义每个操作的函数子==>使它快速。
使其迅速是很难的,但我们有一些技巧。 招数1是只包裹在你的单子自由Codensity
(下称“走得更快按钮”),但是当不工作,你想摆脱的自由表示。 还记得我们有
runState (Pure a) s = (a,s)
runState (Impure (Get f)) s = runState (f s) s
runState (Impure (Set s next)) _ = runState next s
好了,这是从功能Free StateF a
以s -> (a,s)
刚刚使用的结果类型作为我们国家的定义似乎是合理的......但是我们如何定义操作? 在这种情况下,你知道答案,但推导它会去思考什么Conal埃利奥特调用类型的类态射方面的一种方式。 你要
runState (return a) = return a
runState (x >>= f) = (runState x) >>= (runState f)
runState (set x) = set x
runState get = get
这使得它很容易
runState (return a) = (Pure a) = \s -> (a,s)
runState (set x)
= runState (Impure (Set x (Pure ())))
= \_ -> runState (Pure ()) x
= \_ -> (\s -> (a,s)) x
= \_ -> (a,x)
runState get
= runState (Impure (Get Pure))
= \s -> runState (Pure s) s
= \s -> (s,s)
这是相当不错的帮助。 派生>>=
这种方式可能会是困难的,我不会在这里包括,但这些别人正是你所期望的定义。
要回答这个问题,因为它是说,大多数你没有在这个问题提熟悉的单子本身并不是免费的单子。 菲利普JF的回答暗示了你可以如何与给定的单子到一个新的,免费的单子,但新的单子将“大”:它比原来的单子更多不同的值。 例如真正的State s
单子满足get >>= put = return ()
,但对自由单子StateF
没有。 作为一个免费的单子,它不符合定义额外的方程; 即打浆度的想法。
我将表明, Reader r
, Writer w
和State s
单子都没有,除了在特殊条件下免费r
/ w
/ s
。
让我介绍一些术语。 如果m
是单子然后我们称之为类型的任何值ma
的( m
- )动作。 我们叫m
-action平凡的,如果它等于return x
的一些x
; 否则,我们把它叫做平凡的。
如果m = Free f
是一个仿函数任何免费单子f
,然后m
承认单子同态Maybe
。 这是因为Free
是在它的参数函子f
和Maybe = Free (Const ())
其中Const ()
是在函子的类别相关的终端对象。 具体而言,这种同态可以写成
toMaybe :: Free f a -> Maybe a
toMaybe (Pure a) = Just a
toMaybe (Impure _) = Nothing
因为toMaybe
是单子同态,它特别是满足toMaybe (v >> w) = toMaybe v >> toMaybe w
为任何m
-actions v
和w
。 现在观察toMaybe
发送琐碎m
-actions琐碎Maybe
-actions和平凡m
-actions到平凡Maybe
-action Nothing
。 现在Maybe
有,物业>>
荷兰国际集团的任何行动平凡的行动,以任何顺序,产生一个平凡的行动( Nothing >> w = v >> Nothing = Nothing
); 所以同样是真正的m
,因为toMaybe
是一个单子同态,可以保留(非)琐事。
(如果你愿意,你也可以直接从公式验证这个>>
一个免费的单子。)
要说明的是一个特定的单子m
是不是同构到任何免费单子,那么,它足以找到m
-actions v
和w
,使得至少一个v
和w
不是微不足道,但v >> w
是微不足道的。
Reader r
满足v >> w = w
,所以我们只需要挑选w = return ()
任何平凡的行动v
,只要它存在r
至少有两个值(然后ask
是非常数,即非平凡)。
对于Writer w
,假设有一个可逆元件k :: w
比身份其他。 让kinv :: w
是其倒数。 然后tell k >> tell kinv = return ()
,但tell k
并tell kinv
是平凡的。 任何非平凡基团(例如下加入整数)具有这样的元件k
。 我推定该形式的自由单子Writer w
只是那些的量,幺半w
本身是自由的,即, w = [a]
Writer w ~ Free ((,) a)
。
对于State s
,同样的,如果s
承认任何平凡的同构f :: s -> s
与逆finv :: s -> s
,然后modify f >> modify finv = return ()
。 任何类型的s
具有至少两个元件和可判定平等具有这样构。
我写了一个证明,该名单单子是不是在一个自由张贴到Haskell的咖啡厅邮件列表的基础上,在里德的回答见解:
回想一下,我们可以构造,对于任何函子男,在F免费的单子:
data Free f a = Pure a | Roll (f (Free f a))
直观地说,免费发的是A型的叶F形树的类型。 加入操作仅仅是移植物的树木一起,不执行任何进一步的计算。 形式(辊_)的值应在此发布被称为“平凡”。
一些常用的单子都在对一些仿函数其实免费的单子:
树单子是在函子自由单子Pair
,其中data Pair a = MkPair aa
。 直觉是对形树是最强大的在这个例子中。 每个父节点都有一对儿。
该单子也许是在函子自由单子Unit
,其中data Unit a = MkUnit
。 在图形图像父节点有一个Unit
的孩子,所以没有孩子的。 因此,有整整两个Unit
形树:树仅由叶(对应于Just _
)以及由无子女的父节点的三个(相当于Nothing
)。
该Identity
单子是在仿免费单子Void
,其中Void a
是没有构造一个类型。
该Partiality
单子是在免费的单子Maybe
。
所述Writer [r]
单子是在函子自由单子(r,)
在一个(r,)
形树,父节点装饰有类型的值r
和具有正好一个子节点。 遍历该路径产量列表r
的和类型的叶值a
,所以在型的总的值([r],a) = Writer ra
。
(特别是Free (r,) ()
同构于[r]
这个观察导致了要求该列表单子是免费的,但这种说法是错误的,事实上没有从观察效仿。为了表明,单子m
是免费的,一个具有显示出单子同构forall a. ma -> Free fa
一些算符f
。与此相反,表现出一个同构ma
与Free (fa) ()
既不足够也不需要)。
更单子都是免费的单子商。 例如, State s
是一个商Free (StateF s)
其中,
data StateF s a = Put s a | Get (s -> a).
-- NB: This functor is itself a free functor over a type constructor
-- which is sometimes called StateI [1], rendering Free (StateF s) what
-- Oleg and Hiromi Ishii call a "freer monad" [2].
商通过下列单子态射,这给语义到自由单子的纯粹的语法值展出。
run :: Free (StateF s) a -> State s a
run (Pure x) = return x
run (Roll (Put s m)) = put s >> run m
run (Roll (Get k)) = get >>= run . k
这种观点是运算包由apfelmus基础[1],并且还谈到了在StackOverflow的螺纹[3]。 这是最主要的原因,免费单子是在编程方面非常有用。
那么,是不是名单单子一个单子自由? 直观地说,这不是,这是因为加入列表单子(串联)并不仅仅表现嫁接在一起的操作,但它们展平[4]。
这里是一个证明,列表单子是不是免费的。 我记录它,因为我一直感兴趣的很长一段时间的证明,但寻找它没有取得任何结果。 螺纹[3]和[5]走近,虽然。
在通过任何算符免费单子,与任何功能结合一个平凡的行动结果始终是平凡的,即
(Roll _ >>= _) = Roll _
这可以直接从定义检查(>>=)
为自由单子与利用一个单子态射来也许,参见[3]的里德巴顿的特技或者可替换地。
如果列表单子是同构的,作为一种单子超过一些仿免费单子,同构将映射只有单列出[x]
窗体的值(Pure _)
和所有其他列表中不平凡的价值。 这是因为单子同构与“回归”通勤和return x
为[x]
列表中的单子和Pure x
在免费的单子。
这两个事实相互矛盾,因为可以用下面的例子可以看出:
do
b <- [False,True] -- not of the form (return _)
if b
then return 47
else []
-- The result is the singleton list [47], so of the form (return _).
将一个假设的同构超过一些仿免费的单子后,我们不得不将一个平凡的价值(形象的结合[False,True]
同构下),还有一些功能导致琐碎价值(形象[47]
即return 47
)。
欢呼声中,英戈
[1] http://projects.haskell.org/operational/Documentation.html
[2] http://okmij.org/ftp/Haskell/extensible/more.pdf
[3] 可表示为免费对一些仿什么单子?
[4] https://hackage.haskell.org/package/free-4.12.4/docs/Control-Monad-Free.html
[5] https://www.reddit.com/r/haskell/comments/50zvyb/why_is_liststate_not_a_free_monad/
所有的单子可以表示为自由单子。 他们的附加属性(如MonadFix和续)可被剥离,那是因为自由单子不是免费MonadFix或免费兰萨纳。
一般的方法是定义在liftM方面函子的FMAP,然后围绕该函子包免费。 将所得的单子可以通过指定的功能如何被减小到前一个/实际单子return
并join
(纯和不纯)必须被映射到实际的单子。