什么单子可以表示为免费对一些仿函数?(What monads can be expressed as

2019-07-19 02:40发布

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?

Answer 1:

几乎所有的人(最多涉及循环和问题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

这是为什么很酷吧? 那么几件事情

  1. 免费的单子给你一块,你能想到的作为对于一元代码的AST 数据 。 你可以写,在这个数据操作功能,这是DSL的真正有用的
  2. 函子组成,这意味着打破你的单子像这样使他们半composeable。 特别是,鉴于其共享代数2函子(一个代数本质上只是一个功能fa -> a对某些af是一个仿函数),该组合物还具有代数。

函子组成仅仅是我们可以在几个方面,其中大部分保留该代数结合仿函数。 在这种情况下,我们希望不函子的组合物 (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 as -> (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)

这是相当不错的帮助。 派生>>=这种方式可能会是困难的,我不会在这里包括,但这些别人正是你所期望的定义。



Answer 2:

要回答这个问题,因为它是说,大多数你没有在这个问题提熟悉的单子本身并不是免费的单子。 菲利普JF的回答暗示了你可以如何与给定的单子到一个新的,免费的单子,但新的单子将“大”:它比原来的单子更多不同的值。 例如真正的State s单子满足get >>= put = return () ,但对自由单子StateF没有。 作为一个免费的单子,它不符合定义额外的方程; 即打浆度的想法。

我将表明, Reader rWriter wState s单子都没有,除了在特殊条件下免费r / w / s

让我介绍一些术语。 如果m是单子然后我们称之为类型的任何值ma的( m - )动作。 我们叫m -action平凡的,如果它等于return x的一些x ; 否则,我们把它叫做平凡的。

如果m = Free f是一个仿函数任何免费单子f ,然后m承认单子同态Maybe 。 这是因为Free是在它的参数函子fMaybe = 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 vw 。 现在观察toMaybe发送琐碎m -actions琐碎Maybe -actions和平凡m -actions到平凡Maybe -action Nothing 。 现在Maybe有,物业>>荷兰国际集团的任何行动平凡的行动,以任何顺序,产生一个平凡的行动( Nothing >> w = v >> Nothing = Nothing ); 所以同样是真正的m ,因为toMaybe是一个单子同态,可以保留(非)琐事。

(如果你愿意,你也可以直接从公式验证这个>>一个免费的单子。)

要说明的是一个特定的单子m是不是同构到任何免费单子,那么,它足以找到m -actions vw ,使得至少一个vw不是微不足道,但v >> w是微不足道的。

Reader r满足v >> w = w ,所以我们只需要挑选w = return ()任何平凡的行动v ,只要它存在r至少有两个值(然后ask是非常数,即非平凡)。

对于Writer w ,假设有一个可逆元件k :: w比身份其他。 让kinv :: w是其倒数。 然后tell k >> tell kinv = return () ,但tell ktell 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具有至少两个元件和可判定平等具有这样构。



Answer 3:

我写了一个证明,该名单单子是不是在一个自由张贴到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 。与此相反,表现出一个同构maFree (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/



Answer 4:

所有的单子可以表示为自由单子。 他们的附加属性(如MonadFix和续)可被剥离,那是因为自由单子不是免费MonadFix或免费兰萨纳。

一般的方法是定义在liftM方面函子的FMAP,然后围绕该函子包免费。 将所得的单子可以通过指定的功能如何被减小到前一个/实际单子returnjoin (纯和不纯)必须被映射到实际的单子。



文章来源: What monads can be expressed as Free over some functor?