Functional programming 函数式编程中的并行函数组合
这个问题是一个更大问题的子问题,这是最后一个难题: 我有两个职能:Functional programming 函数式编程中的并行函数组合,functional-programming,theory,Functional Programming,Theory,这个问题是一个更大问题的子问题,这是最后一个难题: 我有两个职能: getUserFromDB::Token->任一字符串用户 getNewUser::Token->任一字符串用户 如何将它们结合起来,得到如下函数: getUser::Token->任一字符串用户 因此,getUserFromDB和getNewUser依次尝试,输入相同的输入(Token是OAuth api的api标记),并返回第一个成功函数的结果,如果没有成功,返回一些默认值 换句话说:如果FP中有一个签名如下的函数: t
getUserFromDB::Token->任一字符串用户
getNewUser::Token->任一字符串用户
如何将它们结合起来,得到如下函数:
getUser::Token->任一字符串用户
因此,getUserFromDB
和getNewUser
依次尝试,输入相同的输入(Token
是OAuth api的api标记),并返回第一个成功函数的结果,如果没有成功,返回一些默认值
换句话说:如果FP中有一个签名如下的函数:
tryUntilGoodOrFail::failValue->(结果->布尔)->[fn]->输入->结果
或
tryUntilGoodOrFail::a->(a->Boolean)->[(b->a)]->b->a
编辑
这只是一个关于函数式编程的新手问题。我只是不知道如何将两个(或更多)函数以并行顺序放置,这与连续函数管道(FP非常擅长)形成对比。但在现实世界中,这种并行序列函数模式是经常需要的。我相信FP有自己的解决方案。我到处都找不到。问题的一部分是
它们都是右偏的,这意味着它们被设计成在第一个左边短路,但你希望它在第一个右边短路。如果您将所有内容都设置为任一用户字符串
,则您的getUser
将是:
getUser token = getUserFromDb token >> getNewUser token
如果您想将其保持为字符串用户
,则必须交换它,然后绑定,或者使用If
表达式或其他类似的东西自行短路。这听起来像(也是)。假设getNewUser
将返回Left
(或Failure
,具体取决于语言),如果用户已经在系统中,那么您可以使用函数列表,只需按顺序应用它们,然后返回结果列表。然后,您可以找到第一个成功的结果,而不必关心它是新用户还是现有用户
这并没有短路,但它做了次好的事情。我找到的最简单的解决方案(适用于任何懒惰或不懒惰的语言):fold
(foldl
,更准确地说)。不知道如何用Haskell或PureScript来表达它。以下是我的Python版本:
getUser=lambda令牌:折叠(
lambda e,f:e如果e.isRight else f(令牌),
左(‘无工作’),
[getUserFromDB、getNewUser、SomeOther尝试,…]
)
JS也一样:
const getUser=token=>fold(
(e,f)=>e.isRight?e:f(令牌),
左(‘无工作’),
[getUserFromDB、getNewUser、SomeOther尝试,…]
)
PureScript有一个有趣的函数,名为until
,它也可以提供帮助。但是它会返回一个结果列表,直到第一个成功的结果(这是我一直在寻找的),我可以将它提供给last
和maybe
,并获得第一个好结果或默认值。但是,fold(折叠)
一次完成了这一切,并且更为通用、普遍和标准。为什么这被贴上了“理论”的标签?这听起来很像一个特定的Haskell问题。这是一个语言不可知的问题,通常与FP有关。我使用Scala,但听起来这可以通过模式匹配轻松完成。Scala中的最后一个错误类型将被编写为这两种错误类型的超类。我不知道如何在Haskell中编写它。@StasShepelev在Haskell中,或者是的一个实例,并且有一个,所以您只需编写fromRight failValue$getUserFromDB token getNewUser token
@StasShepelev对于更通用的版本,您还可以执行let(右结果)=sconcat$map($token)[getUserFromDB,getNewUser]|:右故障值
Wow!这是我对这个问题的第一个有意义和有教育意义的回答!非常感谢。你知道这种模式在FP中是否不受欢迎吗?我找不到任何“教科书式”的解决办法。在其中一个用例中左右切换或为该用例创建一些特殊容器似乎太过分了。顺便说一句,getUserFromDb-token
将调用(执行)函数,如果我正确地执行了该函数,那么在此之后您不能>
调用它们。或者你能吗?在FP中,值是不需要参数的函数!对吗?getUserFromDb-token>>getNewUser-token
中的getUserFromDb-token
和getNewUser-token
是否意味着这两个函数都可以执行?只有当getUserFromDb
失败时,我才需要执行getNewUser
。默认情况下Haskell是懒惰的。除非需要,否则不会执行第二个函数。Result
列表是否意味着将执行输入列表中的所有函数?这意味着所有函数都可以成功,新用户将在数据库中创建,即使getUserFromDB
将找到搜索到的用户,我们实际上不需要新用户。我假设,如果语言是懒惰的,比如Haskell,那么这种情况就不会发生,但不适合非懒惰语言。是的。这就是应用程序的本质。这就是为什么我说如果用户已经存在,getNewUser
应该返回一个Left
。或者,当找到/创建用户时,您可以使用普通的绑定
,并从每个函数返回左
,当没有找到/创建用户时,返回右
。这将使第一个左侧的链短路
,从而允许令牌
被传递到下一个阶段以供其尝试。