本文介绍了Monad定义中的样板代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于Functor-Applicative-Monad Proposal,Monad是应用类的子类,而应用类又是函数子类。从数学上讲,这似乎是一个明智的选择,我对此没有任何问题。

然而,令我恼火的是,即使fmappure<*>的相应定律无论如何都是由单子定律固定的,也需要写下函数式和应用实例。事实上,在上面的链接提案中,它们自己写道:&您只需添加以下代码即可从Monad派生这些实例:

import Control.Applicative (Applicative(..))
import Control.Monad       (liftM, ap)

instance Functor m where
    fmap = liftM

instance Applicative m where
    pure  = {- move the definition of `return` from the `Monad` instance here -}
    (<*>) = ap

在大多数教程中,您只会看到等号pure=return,然后人们会像前面一样在Monad实例中定义return。因此,实际上,为每个只想定义Monad实例的人添加了10行样板代码。

在数学中,人们通常也不会这样做。例如,当定义某个环的合成律时,通常不会再次明确地提醒读者环是加法下的阿贝尔群的特殊实例,而阿贝尔群又是群的特殊实例。无论如何,这在定义上是明确的。

因此我的问题是:即使monad是Applicative的子类,而Applicative又是Functor的子类,这种情况不是可以在后台发生吗,因为编译器插入的是样板代码,而不是程序员?

推荐答案

文档中的建议是针对过去定义了Monad实例但没有定义Applicative实例的遗留类型。然后根据现有的Monad定义这些实例,这是使包重新编译的一种快速可靠的修复方法。

然而,这不是新代码应该采用的方式,因为Monad实际上是更高级的类。推荐的方式是

  1. 派生Functor实例。此操作始终只有一种可能,而编译器可以为您完成此操作。

    {-# LANGUAGE DeriveFunctor #-}
    data YourMonadType a = ...
      deriving (Functor)
    
  2. 定义Applicative。这并不是完全自动的,有时<*>可能有点违反直觉,但您会掌握它的诀窍,直接实现实际上可能比(<*>) = ap更高效。

    instance Applicative YourMonadType where
      pure = {- direct definition here -}
      q <*> r = {- direct definition here -}
    
  3. 定义Monad。这根本不需要return(因为它是默认的= pure),只需要>>=

    instance Monad YourMonadType where
      q >>= f = {- direct definition here -}
    
    附注:可以说Monad的方法实际上应该是join,而不是>>=。虽然return>>=产生了所有的FunctorApplicative方法,但join是它们的"正交特征"。


在现代GHC中,还可以通过对WrappedMonadNewtype:

使用via策略来获得Applicative的派生实例
{-# LANGUAGE DerivingVia, DeriveFunctor #-}

import Control.Applicative

data YourMonadType a = YourMonadType a a
  deriving stock (Functor, Show)
  deriving Applicative via (WrappedMonad YourMonadType)

instance Monad YourMonadType where
  return x = YourMonadType x x
  YourMonadType x y >>= f = YourMonadType fx fy
   where YourMonadType fx _ = f x
         YourMonadType _ fy = f y
ghci> YourMonadType (+1) (*2) <*> YourMonadType 10 20
YourMonadType 11 40

这篇关于Monad定义中的样板代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-03 07:55