功能构成与Monads ......无法正常工作

问题描述 投票:2回答:1

我有一些丑陋的数据,需要大量丑陋的空检查。我的目标是编写一套函数来以无点的声明式样式访问/修改它,使用Maybe monad将空检查保持在最低限度。理想情况下,我可以将Ramda与monad一起使用,但它的效果并不是很好。

这有效:

const Maybe = require('maybe');
const R = require('ramda');
const curry = fn => (...args) => fn.bind(null, ...args);
const map = curry((fn, monad) => (monad.isNothing()) ? monad : Maybe(fn(monad.value())));
const pipe = (...fns) => acc => fns.reduce((m, f) => map(f)(m), acc);
const getOrElse = curry((opt, monad) => monad.isNothing() ? opt : monad.value());
const Either = (val, d) => val ? val : d;

const fullName = (person, alternative, index) => R.pipe(
  map(R.prop('names')),
  map(R.nth(Either(index, 0))),
  map(R.prop('value')),
  map(R.split('/')),
  map(R.join('')),
  getOrElse(Either(alternative, ''))
)(Maybe(person));

但是,不得不输出十亿次'map()'看起来不是很干,看起来也不是很好。我宁愿有一个特殊的管道/组合函数来包装map()中的每个函数。

注意我是如何使用R.pipe()而不是我的自定义管道()?我的自定义实现总是抛出一个错误,'isNothing()不是函数',在执行传递给它的最后一个函数时。

我不确定这里出了什么问题,或者有更好的方法可以做到这一点,但任何建议都表示赞赏!

javascript functional-programming ramda.js
1个回答
8
投票

首先要做的事情

  1. Maybe实现(link)几乎是垃圾 - 你可能想考虑选择一个不需要你实现Functor接口的实现(就像你用map做的那样) - 我可能会建议来自民间故事的Data.Maybe。或者因为你显然不害怕自己实施的东西,所以制作你自己的Maybe ^ _ ^

  1. 您的map实现不适用于任何实现仿函数接口的仿函数。即,你的只适用于Maybe,但map应该足够通用,可以与任何可映射的一起工作,如果有这样的话。 不用担心,Ramda在框中包含map - 只需将其与Maybe一起使用即可实现.map方法(例如上面引用的Data.Maybe)

  1. 你的curry实现并没有很好地理解函数。它仅适用于arity为2的函数 - 咖喱应适用于任何函数长度。 // given, f const f = (a,b,c) => a + b + c // what yours does curry (f) (1) (2) (3) // => Error: curry(...)(...)(...) is not a function // because curry (f) (1) (2) // => NaN // what it should do curry (f) (1) (2) (3) // => 6 如果你已经在使用Ramda,你真的没有理由自己实现curry,因为它已经包含了curry

  1. 你的pipe实现混合了函数组合和映射函数的问题(通过使用map)。我建议专门为功能组成保留pipe。 再次,不知道为什么你使用Ramda然后重新发明它。拉姆达已经包括pipe 我注意到的另一件事 // you're doing R.pipe (a,b,c) (Maybe(x)) // but that's the same as R.pipe (Maybe,a,b,c) (x)

  1. 你制作的那个Either可能不是你想到的任何一个仿函数/ monad。有关更完整的实现,请参阅Data.Either(来自民间故事)

  1. 没有观察到单个monad - 你的问题是关于monad的函数组合,但你只在代码中使用functor接口。这里的一些混淆可能来自Maybe实现FunctorMonad的事实,因此它可以表现为两者(和它实现的任何其他接口一样)!在这种情况下,Either也是如此。 您可能希望查看Kleisli category的monadic函数组合,尽管它可能与此特定问题无关。

功能接口受法律约束

你的问题源于缺乏对仿函数法则的曝光/理解 - 如果你的数据类型符合这些法则,这意味着什么,只有这样才能说你的类型是一个仿函数。在所有其他情况下,您可能正在处理像仿函数这样的东西,但实际上并不是仿函数。

算子法则

其中map :: Functor f => (a -> b) -> f a -> f bid是身份函数a -> af :: b -> cg :: a -> b

// identity
map(id) == id

// composition 
compose(map(f), map(g)) == map(compose(f, g))

这对我们说的是,我们可以单独为每个函数组合多次调用map,或者我们可以首先组合所有函数,然后map。 - 注意组成法的左侧我们如何调用.map两次来应用两个函数,但在右侧.map只被调用一次。每个表达式的结果是相同的。

monad法律

在我们处理它的同时,我们也可以涵盖monad法则 - 再次,如果你的数据类型遵守这些法则,那么它只能被称为monad。

mreturn :: Monad m => a -> m ambind :: Monad m => m a -> (a -> m b) -> m b

// left identity
mbind(mreturn(x), f) == f(x)

// right identity
mbind(m, mreturn) == m

// associativity
mbind(mbind(m, f), g) == mbind(m, x => mbind(f(x), g))

使用Kleisli组合函数composek可能更容易看到法则 - 现在Monads真正遵守关联法则是显而易见的

使用Kleisli组合定义的monad法则

哪里有composek :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)

// kleisli left identity
composek(mreturn, f) == f

// kleisli right identity
composek(f, mreturn) == f

// kleisli associativity
composek(composek(f, g), h) == composek(f, composek(g, h))

找到解决方案

那么这对你来说意味着什么呢?简而言之,你做的工作比以往要多 - 特别是实施了你所选择的图书馆Ramda所带来的许多东西。现在,这没有什么不妥(事实上,如果你在网站上审核我的许多其他答案,我就是这方面的一个巨大的支持者),但如果你得到一些错误的实施,它可能是混乱的根源。

由于你似乎大多挂在map方面,我会帮你看到一个简单的转变。这利用了上面说明的Functor组合法:

请注意,这使用R.pipe,它从左到右而不是从右到左组成R.compose。虽然I prefer right-to-left composition,使用pipecompose的选择取决于你 - 这只是一个符号差异;无论哪种方式,法律都得到了满足。

// this
R.pipe(map(f), map(g), map(h), map(i)) (Maybe(x))

// is the same as
Maybe(x).map(R.pipe(f,g,h,i))

我想提供更多帮助,但我不能100%确定你的功能实际上是在做什么。

  1. Maybe(person)开始
  2. 阅读person.names财产
  3. 得到person.names的第一个索引 - 它是一个数组还是什么?或者名字的第一个字母?
  4. 阅读.value财产?我们在这里期待一个单子? (看看.chain.map相比MaybeEither实现我从民间故事中链接)
  5. 拆分/上的值
  6. ''加入值
  7. 如果我们有一个值,则返回它,否则返回一些替代值

这是我对正在发生的事情的最好猜测,但我不能在这里描绘您的数据或理解您正在尝试的计算。如果您提供更具体的数据示例和预期输出,我可能能够帮助您制定更具体的答案。


备注

几年前我也在你的船上;我的意思是,进入函数式编程。我想知道所有小部件是如何组合在一起的,并且实际上产生了一个人类可读的程序。

只有当功能技术应用于整个系统时,才能观察到函数式编程提供的大多数好处。首先,你会觉得你必须引入大量的依赖,只是以“功能方式”重写一个函数。但是一旦你在程序中的更多地方发挥了这些依赖关系,你就可以开始左右削减复杂性。这真的很酷,但需要一段时间才能让你的程序(以及你的头脑)在那里。

事后看来,这可能不是一个好的答案,但我希望这能帮助你。这对我来说是一个非常有趣的话题,我很乐意协助回答你的任何其他问题^ _ ^

© www.soinside.com 2019 - 2024. All rights reserved.