monadic liftM和functorial fmap必须是等价的吗?

Tom*_*Tom 7 haskell category-theory

(注意:我正在使用Haskell术语来表达这个问题;欢迎使用相同的术语和/或类别理论的数学语言,包括正确的数学定义和公理,其中我提到了仿函数和monad定律.)

众所周知,每个monad也是一个仿函数,仿函数与fmapmonad相当liftM.这是有道理的,当然适用于所有常见/合理的monad实例.

我的问题是这种等效性fmapliftM可证明是否遵循了仿函数和monad定律.如果是这样,很高兴看到如何,如果不是,那么看到一个反例会很好.

为了澄清,我所知的仿函数和monad定律如下:

  • fmap idid
  • fmap f . fmap gfmap (f . g)
  • return x >>= ff x
  • x >>= returnx
  • (x >>= f) >>= gx >>= (\x -> f x >>= g)

我没有看到这些法律中将函数功能(fmap)与monad功能(return>>=)相关联,因此我发现很难看出如何从它们中导出等价fmapliftM(定义为liftM f x = x >>= (return . f)).也许有一个争论,这对我来说不够简单?或许我错过了一些法律?

luq*_*qui 10

您错过的是参数性定律,也称为自由定理.参数化的一个后果是所有多态函数都是自然变换.Naturality表示形式的任何多态函数

t :: F a -> G a
Run Code Online (Sandbox Code Playgroud)

在哪里FG是玩家,通勤fmap:

t . fmap f = fmap f . t
Run Code Online (Sandbox Code Playgroud)

如果我们能够做一些liftM具有自然变换形式的事物,那么我们将得到一个关于liftM和的方程式fmap.liftM本身不会产生自然转变:

liftM :: (a -> b) -> m a -> m b
              --       ^______^
              -- these need to be the same
Run Code Online (Sandbox Code Playgroud)

但这是一个想法,因为它(a ->)是一个仿函数:

as :: m a
flip liftM as :: (a -> b) -> m b
              --  F    b  -> G b
Run Code Online (Sandbox Code Playgroud)

让我们尝试使用参数flip liftM m:

flip liftM m . fmap f = fmap f . flip liftM m
Run Code Online (Sandbox Code Playgroud)

前者fmap(a ->)仿函数fmap = (.),所以

flip liftM m . (.) f = fmap f . flip liftM m
Run Code Online (Sandbox Code Playgroud)

Eta扩展

(flip liftM m . (.) f) g = (fmap f . flip liftM m) g
flip liftM m (f . g)     = fmap f (flip liftM m g)
liftM (f . g) m          = fmap f (liftM g m)
Run Code Online (Sandbox Code Playgroud)

这很有希望.拿g = id:

liftM (f . id) m = fmap f (liftM id m)
liftM f m        = fmap f (liftM id m)
Run Code Online (Sandbox Code Playgroud)

这就足够了liftM id = id.这可能源于其定义:

liftM id m
   = m >>= return . id
   = m >>= return
   = m
Run Code Online (Sandbox Code Playgroud)

是的!QED.

  • 对.参数化意味着带有`id`到`id`的`fmap`类型的函数是唯一的,并且从`liftM`的定义它遵循`liftM id = id`,因此`liftM = fmap`.参看 [我对一个非常相似的问题的回答](/sf/answers/3087420591/). (6认同)
  • 所以,只是为了澄清:你可以将一个monad定义为一个类型构造函数`m`,它有两个多态函数`>> =`和`return`; 并且它自动成为一个仿函数,其动作是由"liftM"定义的态射.问题是:如果我定义另一个仿函数,它对对象的动作(类型构造函数`m`)是相同的,但它对态射(`fmap`)的作用是不同的呢?您可以在类别理论中自由地做到这一点,但是如果您尝试在Haskell中执行此操作,则会受到参数化的限制,这会告诉您给定类型构造函数的`fmap`是唯一的,只要它将身份映射到身份. (2认同)

chi*_*chi 6

在本练习中,我发现使用它join比使用它更容易>>=.monad可以通过return并且join满足来等效地定义

1) join . join = join . fmap join
2) join . return = join . fmap return = id
Run Code Online (Sandbox Code Playgroud)

确实,join并且>>=是可以相互定义的:

x >>= f = join (fmap f x)
join x = x >>= id
Run Code Online (Sandbox Code Playgroud)

你提到的法律与上述法律相对应(我不会证明这一点).

然后,我们有:

liftM f x
= { def liftM }
x >>= return . f 
= { def >>= }
join (fmap (return . f) x)
= { def . and $ }
join . fmap (return . f) $ x
= { fmap law }
join . fmap return . fmap f $ x
= { join law 2 }
id . fmap f $ x
= { def id, ., $ }
fmap f x
Run Code Online (Sandbox Code Playgroud)

  • 你正在回答一个稍微不同的问题.使用`join`和`return`定义monad也需要`fmap`.另一方面,使用这三个你可以定义`liftM`.你表明这个'liftM`和原来的'fmap`是一样的.这不需要参数.在任何类别中都是如此. (2认同)