最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 面向 JavaScript 开发人员的 Monads

    正文概述 掘金(Zz招锦)   2021-03-09   553

    面向 JavaScript 开发人员的 Monads

    面向 JavaScript 开发人员的 Monads

    和别的程序员一样,我也很想知道 Monads 到底是什么。但每当我在网上搜索 Monads 的时候,都只会找到大量的 Monads 理论范畴文章,而其它的资源似乎也并没有什么参考意义。

    为了搞清楚什么是 Monads,我花费了大量的时间精力。我开始去学习 Haskell,但在学了几个月后,我才突然意识到,大家都在 Monads 上面太小题大做了。如果你是一名 JavaScript 开发人员,那么你肯定每天都在使用它,只是你并没有意识到而已。


    本文并不会提及太多有关 Monads 的理论范畴或 Haskell 的细节,但有一件事我们始终需要知道 —— 当我们在互联网上搜索 Monads 时,不能错过这个定义:

    (>>=) :: m a -> (a -> m b) -> m b
    

    这是 Haskell 中 bind 运算符的定义。不同的语言对这个操作有不同的叫法,但意思都是一样的。比如一些替代名称是 chainbindflatMapthenandThen

    Monadic 上下文

    (>>=) :: m a -> (a -> m b) -> m b
    
    m    :: monadic 上下文
    a, b :: 上下文的值 (string, number, ..)
    

    Monadic 上下文(Monadic Context) 只是一个盒子,它实现了使该盒子成为一个 Monad 所需的全部功能。一个很简单的(非 Monadic)盒子可能是这样的:

    const Box = val => ({ val }); 
    const foo = Box("John");
    

    这是一个只包装了值的盒子,该盒子没有任何功能,因为它没有任何方法实现。

    接着让我们回到 (>>=) :: m a -> (a -> m b) -> m b(>>=) 用作 m a >>= (a -> m b) 的中缀运算符,而 (>>=) 运算的结果是 m b

    存在的问题

    你有没有注意到我们有 m a,但是函数以 a 为参数?这就是 Monads 的意义所在。

    (>>=) 操作是在 Monadic 上下文 m a 中取一个值展开它,所以我们只得到 a,再将其传递给函数 (a -> m b)。这并不奇怪,你还要自己制定这种行为准则,我们稍后会介绍。

    JavaScript 的 Promises

    JavaScript 中的 Promises 类似于 Monads,更确切地说,他们都有 Monad 式(Monad-ish)行为。要成为 Monad,它还必须实现一个仿函数(Functor)和应用程序接口(Applicative)。我提这一点只是为了表述完整,但我们不会更深入地讨论这些。

    JavaScript 的 Promises 使用 .then() 方法实现 Monadic 接口。我们来看以下示例:

    // p :: m a :: Promise { 42 }
    const p = Promise.resolve(42);
    

    这通常会创建一个箱子,在 Promise 中有一个值为 42。 ☝️ 这是我们的 m a

    接着我们有一个将数字除以二的函数,输入的内容没有包装在 Promise 中,但是返回的函数包装在 Promise 中。

    // divideByTwo :: (a -> m b)
    const divideByTwo = val => Promise.resolve(val / 2);
    

    ☝️ 这就是我们的 (a -> m b)

    再次注意,我们在 Promise 中有一个值 42,但是函数 divideByTwo 接受一个未包装的值,并且我们仍然可以链接这些。

    // p :: m a :: Promise { 42 }
    const p = Promise.resolve(42);
    // p2 :: m a :: Promise { 21 }
    const p2 = p.then(divideByTwo);
    // p3 :: m a :: Promise { 10.5 }
    const p3 = p2.then(divideByTwo);
    

    或者更明显的是:

    // p :: m a :: Promise { 10.5 }
    const p4 = p.then(divideByTwo).then(divideByTwo);
    

    这是 Monads 最重要的特性。

    箱子中有一个值 —— Promise { 42 },你有一个采用展开值 42 的函数。m aa 的类型不匹配,你仍然可以将该函数应用于封装的值。

    那怎么可能呢?

    这是因为 Promise 实现 then 方法并以这种方式工作。大多数时候,在 Promise 中运行的代码是异步的。但是 Promise 的单步行为使得它可以链接一系列函数。

    Monads 抽象出辅助数据管理、控制流或函数副作用(side-effects),将可能复杂的函数序列转换成简明的管道。

    自定义 Monad 式类

    我使用 TypeScript 整理了一个非常简单的 Monad 式类例子,它不产生任何函数副作用,并且允许链接函数。

    class Dummy<T> {
      constructor(private val: T) {}
    
      chain<TResult>(fn: (value: T) => Dummy<TResult>): Dummy<TResult> {
        return fn(this.val);
      }
    
      static unit<T>(val: T): Dummy<T> {
        return new Dummy(val);
      }
    }
    
    const d = new Dummy(41);
    d.chain(val => new Dummy(val + 1))
     .chain(val => new Dummy("The answer is: " + val));
    

    Monad 规则

    具有 Monad 特性的类必须遵循一些规则。

    • 左单位元
    • 右单位元
    • 可结合性

    你可以在网上查找更多有关信息。在这里放一段代码,以证明 Dummy 类遵循这些规则。

    const m = Dummy.unit(1);
    const f = (val: number) => new Dummy(val + 1);
    const g = (val: number) => new Dummy(val + 2);
    
    // 1. 左单位元
    Dummy.unit(1).chain(f) ==== f(1)
    
    // 2. 右单位元
    m.chain(Dummy.unit) ==== m
    
    // 3. 可结合性
    const m1 = Dummy.unit(1);
    m.chain(f).chain(g) ==== m.chain(val => f(val).chain(g)
    

    ===== 在这里不起作用,因为对象引用是不同的。为此,我使用不存在的 ====,但可以理解为 Monad 式类的对象的内部值。

    本文总结

    我希望本文能使你了解 Monads,如果你是 JavaScript 开发人员,则每天都会使用它们。例如将 Promise 中装箱的值提供给需要未包装值的函数,并再次返回包装在 Promise 中的新值。

    参考资料

    • en.wikipedia.org/wiki/Monad_…


    起源地下载网 » 面向 JavaScript 开发人员的 Monads

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元