不完整解释 Monad 有什么用

【预警】这篇文章没有详细解释 Monad,我承诺我会抽空写。

关于我为什么自己打脸回来写文章,不想解释太多。只想说掘金真香。

本打算这周末写文章解释下 Monad 的,但是最近比较忙,还是再拖一会。

新工作挑战比较大。第一次遇到这么复杂的业务和开发流程,一开始适应的不是很好。开会全程懵逼,不知道别人在讲什么。最近主要精力还是要花在熟悉业务和新的工作环境,学习分享上会缓一缓。

先简单介绍一下 Monad 的用处预热一下吧。可能看完这篇你不会全懂,那是因为我没仔细解释,留待下次吧,抱歉了。这里要展示的代码主体部分是我的练习改写,后半部辅助函数和示例是我模仿改写的。

function IO(effectFn) {
  const __val = effectFn
  const map = fn => IO(() => fn(__val()))
  const performUnsafeIO = __val
  const chain = fn => IO(() => fn(__val()).performUnsafeIO())
  return Object.freeze({
    map,
    chain,
    performUnsafeIO,
  })
}

const curry = fn => (...args) =>
  args.length >= fn.length ? fn(...args) : curry(fn.bind(undefined, ...args))

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

const map = curry((fn, monad) => monad.map(fn))

const chain = curry((fn, monad) => monad.chain(fn))

const setStyle = curry((sel, props) => IO(() => $(sel).css(props)))

const getItem = key => IO(() => localStorage.getItem(key))

const applyPreferences = compose(
  chain(setStyle('#main')),
  map(JSON.parse),
  getItem
)

applyPreferences('preferences').performUnsafeIO()
复制代码

这个 IO Monad 在一些 ADT 库里面也叫 Effect,它是来处理应用中的作用的。先看示例部分。这个应用的主要功能就是从 localStorage 读取用户的样式偏好,读到之后再改掉页面对应的样式。这个简单例子涉及到两个作用(effects),注意作用和副作用(side effects)是两个不同的概念。这两个作用是读取数据库和改变 DOM 节点样式属性。函数式编程的一个主要挑战就是把计算和作用分离开来,计算的过程中不能产生作用。

回到代码看是怎样做到的。首先 getItem 函数把根据传入的 key 读取 localStorage 的行为扔进了 IO 函数。IO 函数把这个会产生作用的里层函数存在闭包里,并没有立即执行。map 的作用就是,先执行传进 IO 的函数,再把计算结果传进 map 自身接受的回调函数。但是请注意,map 并没有立即执行会产生作用的函数,它只是声明了行为。接着,到了最难理解的 chain 函数(如果你理解了 chain 在干什么,你就完全理解 Monad 了)。chain 接受的回调函数自身也会返回一个 IO,这个时候就不能直接把回调函数执行的结果扔回给 IO 了,不然就是 IO 嵌套 IO,没办法 map 了。所以先把里层 IO 的作用函数执行一遍,再把结果塞回 IO。同样,这里只是声明行为,没有真的执行。

程序运行到 applyPreferences("preferences") 的时候,就把应用功能全部描述完了,但只是定义了每一步的计算,还没开始执行指令。到最后一部 performUnsafeIO 的时候,奇迹才会发生,作用才会释放。再回过头看整个程序,是不是觉得很干净?不管你有没感受到,反正我感受到了……

你可能会问:谁 TM 这样子写代码找抽啊!!!

其实,RxJS 的原理差不多就是这样的。Observable 就是个 IO Monad。RxJS 里面声明的计算,都是惰性的,只有在最后 subscribe 的时候,计算才会被触发,作用才会被释放。

本文示例主要目的是演示,还是过于简单化。注意 JSON.parse 可能会抛出异常,而抛出异常也属于作用。有一个叫 Maybe 的数据类型专用来解决这类问题。我以前在一篇介绍 Ramda 的文章最后面有演示 Maybe 的用法。

线上 Demo 戳这里

这篇文章也发表在我的中文博客上 Lambda Academy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 也许Monad是一种特定的态射,它可以将一个函数(可能是不纯的)映射到一个可能包含一个值或一个空值(Maybe)的可能性。它可以让我们处理函数的可能性,而不是像其他函数一样处理其结果。也就是说,它使我们能够从一个可能存在的值中获取有用的信息,而不是必须处理它的结果或错误。 ### 回答2: Maybe Monad 是一种用于处理可能存在空值的情况的特殊 Monad。在函数式编程中,我们经常会遇到某些函数可能返回 null 或者 undefined 的情况,这可能会导致一些异常或者错误。而 Maybe Monad 就可以帮助我们处理这种情况,使得代码更加健壮和安全。 Maybe Monad 具有两种可能的值:Just 和 Nothing。Just 表示存在一个具体的值,而 Nothing 表示空值。我们可以将带有可能为空值的函数包装进 Maybe Monad 中,通过一系列的操作来确保函数的返回值总是有意义的。 Maybe Monad 可以将不纯的函数转换成一种特殊的映射,这种映射可以连续地处理可能为空值的情况。它提供了一些方法,比如 map、flatMap 和 filter,用于对 Maybe Monad 中的值进行处理。 使用 map 方法,我们可以对 Maybe Monad 中的值进行转换操作。如果 Maybe Monad 是 Just,那么 map 方法会将这个值传递给函数并将返回值包装为新的 Just,否则将返回 Nothing。 使用 flatMap 方法,我们可以进行链式操作。它接受一个函数作为参数,这个函数返回一个新的 Maybe Monad。如果当前 Maybe Monad 是 Just,则将其值传递给函数并返回新的 Maybe Monad,否则直接返回 Nothing。 使用 filter 方法,我们可以对 Maybe Monad 中的值进行条件过滤。如果 Maybe Monad 是 Just 并且满足条件,则返回原始 Maybe Monad,否则返回 Nothing。 通过利用这些方法,我们可以在处理可能为空值的函数时更加灵活。Maybe Monad 的工作原理就是通过这些操作来确保不纯的函数总是可以正常处理为空值的情况,从而避免潜在的错误和异常。 ### 回答3: Maybe Monad是一种用来处理可能存在空值或错误情况的编程模式。它将不纯的函数转换为一种特殊类型的映射,该映射可以处理空值情况。 在Maybe Monad中,我们有两种可能的结果:Just值和Nothing。Just表示有一个非空的值,而Nothing表示无值或空值。 Maybe Monad的工作方式如下:当我们在Maybe Monad中执行一个函数时,首先会检查输入值是否为Nothing。如果输入是Nothing,则函数不会执行任何操作,而是直接返回Nothing。这种处理方式允许我们在遇到空值时,避免出现错误或异常。 如果输入值是Just值,函数将会被执行且返回一个新的Maybe值。这个新的Maybe值可以是Just值,也可以是Nothing。如果函数执行过程中遇到错误,它会返回Nothing;如果没有错误,它会返回一个新的Just值。 使用Maybe Monad的好处是,它能够简化我们在处理可能存在空值的情况下的编程逻辑。它将错误处理与核心逻辑分离,使代码更加清晰和易于理解。 总结起来,Maybe Monad通过将不纯的函数转换为具有特殊处理空值能力的映射,来处理可能存在空值或错误情况。它提供了一种简单且可靠的方式来处理空值,使得代码更加健壮和鲁棒。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值