che*_*ast 2 javascript functional-programming ramda.js
我有一些丑陋的数据,需要大量丑陋的空检查.我的目标是编写一套函数来以无点的声明式样式访问/修改它,使用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));
Run Code Online (Sandbox Code Playgroud)
但是,不得不输出十亿次'map()'看起来不是很干,看起来也不是很好.我宁愿有一个特殊的管道/组合函数来包装map()中的每个函数.
注意我是如何使用R.pipe()而不是我的自定义管道()?我的自定义实现总是抛出一个错误,'isNothing()不是函数 ',在执行传递给它的最后一个函数时.
我不确定这里出了什么问题,或者有更好的方法可以做到这一点,但任何建议都表示赞赏!
首先要做的事情
Maybe实施(链接)是相当多的垃圾-你可能要考虑采摘,不需要你实现仿函数接口(像你这样做了一个实现map) -我可能会建议Data.Maybe从民间故事.或者因为你显然不害怕自己实施的东西,所以制作你自己的Maybe ^ _ ^您的map实现不适用于任何实现仿函数接口的仿函数.即,你的工作只适用Maybe,但如果有这样一个词,它map 应该足够通用,可以与任何可映射的一起工作.
不用担心,Ramda包含map在框中 - 只需使用它和Maybe实现.map方法(例如上面引用的Data.Maybe)
您的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
Run Code Online (Sandbox Code Playgroud)
curry如果你已经在使用Ramda,你真的没有理由自己实施,因为它已经包括咖喱
您的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)
Run Code Online (Sandbox Code Playgroud)Either你做的可能不是要么仿/单子你想的.请参阅Data.Either(来自民间故事)以获得更完整的实施没有观察到单个monad - 你的问题是关于monad的函数组合,但你只在代码中使用functor接口.这里的某些混乱可能会从以下事实出现以后,Maybe工具Functor 和 Monad,因此它可以表现为两种(像任何其他接口它实现了)!Either在这种情况下也是如此.
您可能希望查看Kleisli categorymonadic函数组合,尽管它可能与此特定问题无关.
功能接口受法律约束
你的问题是脱胎于缺乏的仿法律曝光/理解-这些是什么意思是,如果你的数据类型遵循这些法律,只有那么可以把它可以说是你的类型是一个仿函数.在所有其他情况下,您可能正在处理像仿函数这样的东西,但实际上并不是仿函数.
算子法则
其中
map :: Functor f => (a -> b) -> f a -> f b,id是身份功能a -> a,f :: b -> c和g :: a -> bRun Code Online (Sandbox Code Playgroud)// 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 a,mbind :: Monad m => m a -> (a -> m b) -> m bRun Code Online (Sandbox Code Playgroud)// 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)Run Code Online (Sandbox Code Playgroud)// 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.虽然我更喜欢从右到左的构图,但使用pipevs 的选择compose取决于你 - 这只是一个符号差异; 无论哪种方式,法律都得到了满足.
// 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))
Run Code Online (Sandbox Code Playgroud)
我想提供更多帮助,但我不能100%确定你的功能实际上是在做什么.
Maybe(person)person.names财产person.names- 它是一个数组还是什么?或者名字的第一个字母?.value财产?? 我们在这里期待一个单子?(看.chain比.map在Maybe和Either实现我从民间故事的链接)/''这是我对正在发生的事情的最好猜测,但我不能在这里描绘您的数据或理解您正在尝试的计算.如果您提供更具体的数据示例和预期输出,我可能能够帮助您制定更具体的答案.
备注
几年前我也在你的船上; 我的意思是,进入函数式编程.我想知道所有小部件是如何组合在一起的,并且实际上产生了一个人类可读的程序.
只有当功能技术应用于整个系统时,才能观察到函数式编程提供的大多数好处.首先,你会觉得你必须引入大量的依赖,只是以"功能方式"重写一个函数.但是一旦你在程序中的更多地方使用了这些依赖项,你就可以开始左右削减复杂性了.这真的很酷,但需要一段时间才能让你的程序(以及你的头脑)在那里.
事后看来,这可能不是一个好的答案,但我希望这能帮助你.这对我来说是一个非常有趣的话题,我很乐意协助回答你的任何其他问题^ _ ^
| 归档时间: |
|
| 查看次数: |
397 次 |
| 最近记录: |