Day 25 - Reader Monad

今天来介绍 Reader Monad,其主要处理的就是 dependency injection,

痛点

说到 dependency injection 如果最坏的情况就是要将值不断的传下去,但中间的函式都不会用到该值

const toUpper = str => str.toUpperCase();
const exlaim  = str => str.concat('!');
const deps = {
    "i18n": {
        "HELLO FP!!": "嗨 函数式编程!"
    }
}

const f = (str, deps) => deps.i18n[str]
const g = (upper, deps) => f(exlaim(upper), deps)
const h = (str, deps) => g(toUpper(str), deps)

h('hello fp!', deps) // "嗨 函数式编程!"

可以看到 g 以及 h 就只是将 deps 传下去而已,本身根本没用到,这样不是很冗余吗? 而 Reader Monad 就是来解决这个痛点的! 首先先来看看最初的起点 Function Modeling

Functions Modeling

什麽是 Functions Modeling 呢? 回想一下前面几章无论是 Either, Maybe 又或是 IO 都是放入单一型别(Object, String, ...) ,但如果现在想要放入的是函式呢?

举例来说


const Function = (run) => ({
    run,
})

如果想要让 Function 变成是一个 Functor 呢?

const Function = run => ({
    run, 
    map: f => Function(x => f(run(x)))
})

可以看到 map 就是将函式进行 compose,

Function(toUpper)
    .map(exlaim)
    .run('hello fp!') // HELLO FP!!

那想要将两个 Function Monad 进行 compose 呢?

const Function = run => ({
    run,
    map: f => Function(x => f(run(x))),
    chain: f => Function(x => f(run(x)).run(x))
})

接下来就可以将两个 Monad 进行 compose 了!

Function(toUpper)
    .chain(upper => Function(x => exlaim(upper)))
    .run('hello fp!') // HELLO FP!!

也可将 Function 变成 pointed functor

Function.of = x => Fn(() => x)

大家这时应该就会好奇,那 x 是什麽呢? 可以先印出来看看

Function(toUpper)
    .chain(upper => Function(x => console.log(x, exlaim(upper))))
    .run('hello fp!') // hello fp! HELLO FP!!

没错 x 就是原本放入 run 的参数,而这有什麽用呢? 如果现在要做 dependency injection,例如 DB 的 config 注入等等,就可以派上很大的用场!

所以来改写上述痛点!

Function.of('hello fp!')
  .map(toUpper)
  .chain((upper) => Function((deps) => deps.i18n[exlaim(upper)]))
  .run(deps); // 嗨 函数式编程!!

另外,Function ADT 通常会有一个优化的 API,ask,其主要就是取 run 传入的值(deps)

Function.ask = Function(x => x)

再将上面程序码套用 ask

Function.of('hello fp!')
  .map(toUpper)
  .map(exlaim)
  .chain((str) => Function.ask.map((deps) => deps.i18n[str]))
  .run(deps); // 嗨 函数式编程!!

而这也被称为 Reader Monad!

小结

感谢大家阅读!!

Reference

  1. Frontend Master

<<:  第 25 集:Bootstrap 客制化 RFS 响应式文字

>>:  Day 25 : 插件篇 04 — 如何让 Obsidian 自动推荐关联笔记 (下)?介绍我的笔记架构与 Breadcrumbs 实战应用

连续和非连续内存分配之间的区别

在将主内存分配给操作系统中的进程时,有两个主要部分。 在连续内存分配中,进程被分配主内存的顺序块给整...

Day 12 Hooks 们以及作用域的差别!

该文章同步发布於:我的部落格 After Hooks 之前有介绍过 before hooks 的使...

Day13# defer

第 13 天要介绍 defer 是什麽,那麽我们就进入正题吧 ─=≡Σ(((っ゚∀゚)っ defer...

Dictionary 使用array创建与字典取值

缘由: 在职训时老师讲解语法,讲到Dictionary(字典)时,有一种老师说的我都懂,看起来没什麽...

【I Love Vue 】 Day 29 爱荷华博弈任务(十) - Demo

话不多说,赶紧把我们的作品Demo 给我们 铁人学院的业主吧!! Demo 进入主画面 我们可以透过...