如何将Monad实例定义为具有多个值的类型? [英] How to define a Monad instance to types with mutiple values?

查看:95
本文介绍了如何将Monad实例定义为具有多个值的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过多个值我的意思是这样的:

  data Foo a = Bar a | Baz aa 

我无法想出明确的方式来定义> > = for Baz

 实例Monad Foo其中
Bar x>> = f = fx - 太好了,那完美!
Baz x y>> = f = ??? - 我甚至在这里放了什么?


解决方案

也许:

  frst(Bar a)= a 
first(Baz a a')= a

scnd(Bar a)= a
scnd(Baz a a')= a'

实例Monad Foo其中
return = Bar
Bar x>> = f = fx
baz xy> = f = Baz(frst(fx))(scnd(fy))

这个定义受到(>> =)(Bool - >) 。问我是否不清楚如何。



让我们来看看法律。 return 是单位的法则非常简单:

$ $ p $ return x >> = f
= Bar x>> = f
= fx

m>> =返回
= b Bar x - >返回x
Baz x y - > Baz(frst(return x))(scnd(return y))
= case m of
Bar x - > Bar x
Baz x y - > Baz xy
= m

我相信自己对(>> =)也是联想法律,但我相信这个证明对其他人来说是完全不可读的......我鼓励你自己去证明它,

  m>>> =(\v  - > ; fv>> = g)
= case $ m
Bar x - > (\ v→> f v>> = g)x
Baz x y - > Baz(first((\v→fv>≥g)x))
(scnd((\v-> fv>> = g)y))
=案例m的
Bar x - > f x>> = g
Baz x y - > Baz(frst(f x> = g))(scnd(f y>> = g))
=
的情况m Bar x - >案例f x为
Bar y - > g y
Baz a b - > Baz(frst(g a))(scnd(g b))
Baz x y - > Baz(frst l)(scnd r)其中
l = $ f
$ b Bar a - > g a
Baz a b - > Baz(frst(g a))(scnd(g b))
r =
的情况f bar a - > g a
Baz a b - > Baz(frst(g a))(scnd(g b))
= case b
Bar x - >案例f x为
Bar y - > g y
Baz a b - > Baz(frst(g a))(scnd(g b))
Baz x y - > Baz(frst(fx))))
(scnd(g(scnd(f y))))
= case $ m
Bar a - >情况f a是
Bar x - > g x
Baz x y - > Baz(frst(g x))(scnd(g y))
Baz a b - >
Bar x - >的情况Baz(frst(f a))(scnd(f b))。 g x
Baz x y - > Baz(frst(g x))(scnd(g y))
=案例v of
Bar x - > g x
Baz x y - > Baz(frst(g x))(scnd(g y))
其中v = case $ m
Bar a - > f a
Baz a b - > Baz(frst(f a))(scnd(f b))
= case m>> = f
Bar x - > g x
Baz x y - > Baz(frst(gx))(scnd(gy))
=(m>> f)>> = g

编辑好的,我决定写一个简短的解释,说明它是如何受到(Bool - >)即使没有人问。因此,回想一下:

  instance Monad(e  - >)其中
m>> = f = \ e - > f(me)e

现在我们要定义

 数据Pair a = Pair aa 

并观察 Bool - > Pair a 非常相似:

 到::配对a  - > (Bool  - > a)
to(Pair false true)= \ bool - >
错误的案例bool - > false
True - >真正的

来自::(Bool - > a) - >从f = Pair(f False)(f True)



结合
从和是同构的。换句话说:您可以交替考虑 Bool - >一个作为双元素容器。那么,如果我们尝试将 Monad (e - >)实例翻译成 Pair 类型?它当然应该是可能的,因为它们是同构的。实际上,让我们从同构开始:

pre $ 实例Monad Pair其中
return x = from(return x)
m>> = f = from(to m>> = to。f)
p>现在我们可以转动曲柄:

  return x 
= from(return x)
= from(\e-> x)
= Pair((\ e - > x)False)((\ e - > x)True)
= Pair xx



  m @(Pair false true)>> = f 
= from(to m>> = to。f)
= from(\ e - >( f)(给我)e)
=从(\e->到(f(对我))e)
= Pair(g False)(g True)其中
g = \e - > to(f(to me))e
= Pair(to(f(to m False))False)(to(f(to m True))True)
= Pair(case f (false false)真假>假)
(Pair真假 - >真)的情况f(到真真)
= false)
(Pair false false - > true的情况f true)

现在可以通过复制和粘贴上述计算的第一行和最后一行来重写实例而不依赖于(Bool - >)

  frstPair(Pair false true)= false 
scndPair(Pair false true)= true

实例Monad Pair其中
return x = Pair xx
Pair false true>> = f = Pair(frstPair(f false))(scndPair(f true))

希望你能认识到这与上面给出的(>> =)的定义有多相似为 Foo



编辑2 另一个(不同的!) monad为此是可能的。从底部检查同构类型的行为:

  type Foo =产品标识也许
Product 的文档。写出没有同构,它会是:

$ pre $实例Monad Foo其中
return x = Baz xx
Bar x>> = f = Bar(frst(fx))
Baz xy>> = f =
的情况f Bar a - > Bar(frst(f x))
Baz a b - > Baz(frst(fx))b

从某种意义上说,我最初的建议是扩大在添加更多monadic动作时的结果 - 从 Bar > 返回并转换 Bar在绑定中 s不可撤销地为 Baz s - 而此实例在您添加更多monadic操作时收缩可能的结果数量 - 在 return 中转换 Baz 并将 Baz s转换为 Bar s在绑定中不可撤销。相当有趣的设计选择,如果你问我!这也让我怀疑另一个 Monad 实例对于 Product 是否可能(可能对所涉及的函子有不同的约束)。

By multiple values I mean something like so:

data Foo a = Bar a | Baz a a

I can't think of a clear way to define >>= for Baz:

instance Monad Foo where
    Bar x   >>= f = f x -- Great, that works perfectly!
    Baz x y >>= f = ??? -- What the heck do I even put here?

解决方案

Perhaps:

frst (Bar a) = a
frst (Baz a a') = a

scnd (Bar a) = a
scnd (Baz a a') = a'

instance Monad Foo where
    return = Bar
    Bar x >>= f = f x
    Baz x y >>= f = Baz (frst (f x)) (scnd (f y))

This definition is inspired by the definition of (>>=) for (Bool ->). Ask me if it's not clear how.

Let's check the laws. The "return is unit" laws are pretty straightforward:

  return x >>= f
= Bar x >>= f
= f x

  m >>= return
= case m of
      Bar x -> return x
      Baz x y -> Baz (frst (return x)) (scnd (return y))
= case m of
      Bar x -> Bar x
      Baz x y -> Baz x y
= m

I believe I've convinced myself of the "(>>=) is associative" law, too, but I'm sure this proof is completely unreadable to anybody else... I encourage you to try proving it yourself, and refer to my calculations as a cheat-sheet if you get stuck.

  m >>= (\v -> f v >>= g)
= case m of
      Bar x -> (\v -> f v >>= g) x
      Baz x y -> Baz (frst ((\v -> f v >>= g) x))
                     (scnd ((\v -> f v >>= g) y))
= case m of
      Bar x -> f x >>= g
      Baz x y -> Baz (frst (f x >>= g)) (scnd (f y >>= g))
= case m of
      Bar x -> case f x of
          Bar y -> g y
          Baz a b -> Baz (frst (g a)) (scnd (g b))
      Baz x y -> Baz (frst l) (scnd r) where
          l = case f x of
                  Bar a -> g a
                  Baz a b -> Baz (frst (g a)) (scnd (g b))
          r = case f y of
                  Bar a -> g a
                  Baz a b -> Baz (frst (g a)) (scnd (g b))
= case m of
      Bar x -> case f x of
          Bar y -> g y
          Baz a b -> Baz (frst (g a)) (scnd (g b))
      Baz x y -> Baz (frst (g (frst (f x))))
                     (scnd (g (scnd (f y))))
= case m of
      Bar a -> case f a of
          Bar x -> g x
          Baz x y -> Baz (frst (g x)) (scnd (g y))
      Baz a b -> case Baz (frst (f a)) (scnd (f b)) of
          Bar x -> g x
          Baz x y -> Baz (frst (g x)) (scnd (g y))
= case v of
      Bar x -> g x
      Baz x y -> Baz (frst (g x)) (scnd (g y))
  where v = case m of
                Bar a -> f a
                Baz a b -> Baz (frst (f a)) (scnd (f b))
= case m >>= f of
      Bar x -> g x
      Baz x y -> Baz (frst (g x)) (scnd (g y))
= (m >>= f) >>= g

edit Okay, I decided to write a short explanation of how this is inspired by (Bool ->) even though nobody asked. So, recall:

instance Monad (e ->) where
    m >>= f = \e -> f (m e) e

Now we're going to define

data Pair a = Pair a a

and observe that Bool -> a and Pair a are very similar:

to :: Pair a -> (Bool -> a)
to (Pair false true) = \bool -> case bool of
    False -> false
    True  -> true

from :: (Bool -> a) -> Pair a
from f = Pair (f False) (f True)

It turns out that from and to are an isomorphism. In other words: you can alternately think of Bool -> a as a "two-element container". Well, what happens if we try to translate the (e ->) instance for Monad into the Pair type? It certainly ought to be possible, since they're isomorphic. In fact, let's start with the isomorphism:

instance Monad Pair where
    return x = from (return x)
    m >>= f = from (to m >>= to . f)

Now we can "just turn the crank":

  return x
= from (return x)
= from (\e -> x)
= Pair ((\e -> x) False) ((\e -> x) True)
= Pair x x

and:

  m@(Pair false true) >>= f
= from (to m >>= to . f)
= from (\e -> (to . f) (to m e) e)
= from (\e -> to (f (to m e)) e)
= Pair (g False) (g True) where
      g = \e -> to (f (to m e)) e
= Pair (to (f (to m False)) False) (to (f (to m True)) True)
= Pair (case f (to m False) of Pair false true -> false)
       (case f (to m True ) of Pair false true -> true )
= Pair (case f false of Pair false true -> false)
       (case f true  of Pair false true -> true )

So we can now rewrite the instance without relying on (Bool ->) by just copying and pasting the first and last line of the above calculations:

frstPair (Pair false true) = false
scndPair (Pair false true) = true

instance Monad Pair where
    return x = Pair x x
    Pair false true >>= f = Pair (frstPair (f false)) (scndPair (f true))

Hopefully you can recognize how similar this is to the definition of (>>=) I gave above for Foo.

edit 2 Another (different!) monad for this is possible. Check out the behavior of the isomorphic type from base:

type Foo = Product Identity Maybe

See the docs for Product. Written without the isomorphism, it would be:

instance Monad Foo where
    return x = Baz x x
    Bar x >>= f = Bar (frst (f x))
    Baz x y >>= f = case f y of
        Bar a -> Bar (frst (f x))
        Baz a b -> Baz (frst (f x)) b

In a sense, my original proposal "expands" the number of results as you add more monadic actions -- starting with a Bar in return and converting Bars irrevocably to Bazs in the bind -- while this instance "contracts" the number of results possible as you add more monadic actions -- starting with a Baz in return and converting Bazs to Bars irrevocably in the bind. Quite an interesting design choice, if you ask me! It also makes me wonder if another Monad instance for Product is possible (perhaps with different constraints on the functors involved).

这篇关于如何将Monad实例定义为具有多个值的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆