许多常见的单子出现为免费单子,
- 鉴于
data Empty a
,Free Empty
与Identity
monad同构。- 免费
Maybe
可用于建模偏好monad,其中每个图层表示运行计算的时间更长。
使用Free
可以表达哪些其他monad?
我只想到一个:我相信Free (Const e)
与Either e
同构。
修改:使用Free
表达的monad是什么?为什么?
答案 0 :(得分:28)
几乎所有这些(直到涉及循环和mfix
的问题),而不是Cont
。
考虑State
monad
newtype State s a = State (s -> (a,s))
看起来像任何就像一个免费的monad ......但是就你如何使用来考虑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)
我们可以通过将操作列为构造函数
来设计具有此接口的免费monaddata 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
你可以用大多数monad做这个结构。像may / partiality monad由
定义stop :: m a
maybe :: b -> (a -> b) -> m a -> b
规则是,我们将m x
中以x
结尾的每个函数视为仿函数中的构造函数,其他函数是运行生成的自由monad的方法。在这种情况下
data StopF a = StopF deriving Functor
maybe _ f (Pure a) = f a
maybe b _ (Impure Stop) = b
为什么这很酷?好吧一些事情
f a -> a
是一个仿函数时,代数对于某些a
基本上只是一个函数f
),该组合也具有该代数。 Functor组合只是我们可以通过多种方式组合仿函数,其中大部分都保留了代数。在这种情况下,我们不需要组成的仿函数 (f (g (x)))
,而是仿函数coproduct 。函数添加
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
也免费monad保留代数
freeAlg :: (f a -> a) -> Free f a -> a
freeAlg _ (Pure a) = a
freeAlg f (Impure x) = f $ fmap (freeAlg f) x
在Wouter Swierstra的着名论文Data Types A La Carte中,这被用来产生很大的效果。该论文的一个简单例子是计算器。我们将对这篇文章采用monadic take new。鉴于代数
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
您可以定义数字monad
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
和其他所有人类似。
这是一个有用的设计策略:列出你希望monad做的事情==>为每个操作定义仿函数==>弄清楚它的一些代数应该是什么==>为每个操作定义那些仿函数==>快点。
快速加速很难,但我们有一些技巧。诀窍1是将你的免费monad包裹在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 Elliott所谓的类型态射来考虑。你想要
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)
非常有帮助。以这种方式得出>>=
可能很难,我不会在这里包含它,但其他的正是你期望的定义。
答案 1 :(得分:7)
如上所述,要回答这个问题,你在问题中没有提到的大多数熟悉的单子都不是自由的单子。菲利普JF的答案提到你如何将一个给定的monad与一个新的,免费的monad联系起来,但是新的monad将会更大"它具有比原始monad更明显的价值。例如,真正的State s
monad满足get >>= put = return ()
,但StateF
上的免费monad不满足Reader r
。作为一个自由的monad,它不能满足额外的方程式;这就是自由的概念。
除非在Writer w
/ State s
/ {的特殊情况下,我才会显示r
,w
和s
monad不是免费的{1}}。
让我介绍一些术语。如果m
是monad,那么我们会调用类型m a
和(m
- )操作的任何值。如果某个m
等于return x
,我们会将x
- 行动称为行动。否则我们称之为不平凡。
如果m = Free f
是仿函数f
上的任意免费monad,那么m
会允许monad同态到Maybe
。这是因为Free
在其参数f
和Maybe = Free (Const ())
中是函数的,其中Const ()
是仿函数类别中的终端对象。具体地说,这种同态可以写成
toMaybe :: Free f a -> Maybe a
toMaybe (Pure a) = Just a
toMaybe (Impure _) = Nothing
由于toMaybe
是monad同态,因此对于任何toMaybe (v >> w) = toMaybe v >> toMaybe w
- 动作m
和v
,它尤其满足w
。现在观察toMaybe
发送琐碎的m
- 操作到琐碎的Maybe
- 操作和不平凡的m
- 操作到不平凡的Maybe
- 操作Nothing
。现在,Maybe
具有以下任一操作>>
执行任何操作的非常重要操作的属性,产生一个非常重要的操作(Nothing >> w = v >> Nothing = Nothing
);因此m
也是如此,因为toMaybe
是一个保留(非)平凡性的monad同态。
(如果您愿意,也可以直接从>>
的公式中为免费monad验证这一点。)
要显示特定的monad m
与任何免费monad不同构,那么,只需查找m
- 动作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
形式的免费monad只是幺半群w
本身自由的那些,即w = [a]
,Writer w ~ Free ((,) a)
。
对于State s
,同样如果s
允许任何非平凡的自同构f :: s -> s
反向finv :: s -> s
,那么modify f >> modify finv = return ()
。具有至少两个元素和可判定的相等性的任何类型s
都具有这样的自同构。
答案 2 :(得分:2)
根据Reid的回答中的见解,我写了一个证据,证明monad在posting to the haskell-cafe mailing list中不是免费的:
回想一下,对于任何仿函数f,我们可以为f:
构建免费monaddata Free f a = Pure a | Roll (f (Free f a))
直观地,Free f a是具有a型叶子的f形树的类型。 连接操作仅将树木移植到一起并且不执行任何操作 进一步的计算。应调用表格(Roll _)的值 "非平凡"在这篇文章中。
一些通常的monad实际上是一些仿函数的免费monad:
Tree monad是仿函数Pair
上的免费monad,其中
data Pair a = MkPair a a
。直觉就像双形树
在这个例子中是最强大的。每个父节点都有一对
孩子。
Maybe monad是仿函数Unit
上的免费monad,其中
data Unit a = MkUnit
。在图形图片中,父节点有一个
Unit
孩子,所以根本没有孩子。所以恰好有两个
Unit
- 形状的树:仅由叶子组成的树(对应的
到Just _
)以及由无子节点父节点组成的三个节点
(对应Nothing
)。
Identity
monad是仿函数Void
上的免费monad,
其中Void a
是没有构造函数的类型。
Partiality
monad是Maybe
上的免费monad。
Writer [r]
monad是仿函数(r,)
上的免费monad。
在(r,)
形状的树中,父节点用值装饰
类型r
并且只有一个子节点。穿越这样的道路
生成r
的列表和类型a
的叶值,总计如此
类型为([r],a) = Writer r a
的值。
(特别是Free (r,) ()
与[r]
同构。这个观察结果
导致名单Monad是免费的。但这种说法是错误的
并且实际上并没有从观察中得出结论。显示monad m
是免费的,必须表现出monad同构forall a. m a -> Free f a
对于一些仿函数f
。相反,表现出m a
的同构
Free (f a) ()
既不充分也不必要。)
更多的monad是免费monad的商。例如,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].
该商由以下monad态射展示,其给出 语义学对于自由monad的纯粹语法价值。
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]中讨论过。它是主要的 免费monad在编程环境中有用的原因。
那么,monad列表是免费的monad吗?直觉上,它不是,因为 列表monad(连接)的连接操作并不仅仅是移植 表达在一起,但使它们变得扁平[4]。
这是一个证明列表monad不是免费的。我记不起来了 我已经有很长一段时间对证据感兴趣,但正在寻找 它没有产生任何结果。然而,线程[3]和[5]接近了。
在任何仿函数的免费monad中,绑定一个非常重要的结果 任何函数的动作总是非常重要,即
(Roll _ >>= _) = Roll _
可以直接从(>>=)
的定义中免费查看
monad或者Reid Barton利用monad的伎俩
对于Maybe的态射,见[3]。
如果列表monad对于自由monad是同构的a-monad而不是某些
仿函数,同构只会将单例列表[x]
映射到值
形式(Pure _)
和所有其他列表到非平凡值。这是
因为monad isomorphisms必须与" return"和return x
列表monad中为[x]
,免费monad中为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 _).
对一些自由monad应用假设的同构
仿函数,我们有一个非平凡的价值(图像
在同构下的[False,True]
和一些函数导致a
平凡的价值([47]
的图像,即return 47
)。
干杯, INGO
[1] http://projects.haskell.org/operational/Documentation.html
[2] http://okmij.org/ftp/Haskell/extensible/more.pdf
[3] What monads can be expressed as Free over some functor?
[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/
答案 3 :(得分:0)
所有monad都可以表示为Free Monad。他们的附加属性(如MonadFix和Cont)可能会被剥夺,因为Free Monad不是免费的MonadFix或Free Cont。
一般的方法是根据liftM定义Functor的fmap,然后在Functor周围自由包装。
通过指定函数return
和join
(纯净和不纯)必须如何映射到实际的Monad,可以将生成的Monad缩减为前一个/实际Monad。