Scala 如何将返回的函数与诸如`和`之类的运算符进行一般性的链式连接?
问题:链接多个返回函数,其中Scala 如何将返回的函数与诸如`和`之类的运算符进行一般性的链式连接?,scala,Scala,问题:链接多个返回函数,其中左侧的都是从一个公共密封特征内部错误继承的失败。但是,编译器抱怨链返回的是或[\uu,成功],而不是或[InternalError,Success] 以下是进行链接的代码: import scala.language.implicitConversions object EitherExtension { implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) { //get a
左侧的都是从一个公共密封特征内部错误继承的失败。但是,编译器抱怨链返回的是或[\uu,成功]
,而不是或[InternalError,Success]
以下是进行链接的代码:
import scala.language.implicitConversions
object EitherExtension {
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
//get ability to chain/compose functions that return aligning Eithers
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
}
正如在评论中指出的,这将丢弃Left
类型。如果我在下面更改它,它将不起作用,因为最终输出的类型可以是other[X | Y,C]
,解析为other[|Y,C]
,我回到了第一步
implicit class AndThenEither[A,B,X](val e: (A) => Either[X, B]) {
def andThenE[C,Y](f:(B) => Either[Y, C]): (A) => Either[_, C] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
下面是显示类型对齐组合失败的示例:
import EitherExtension._
object AndThenComposition {
//sample type definitions of failure responses
sealed trait InternalError
case class Failure1() extends InternalError
case class Failure2() extends InternalError
//sample type definitions
case class Id(id: Int)
case class Stuff()
//sample type definitions of successful responses
case class Output1()
case class Output2()
case class InputRequest()
val function1: (InputRequest) => Either[Failure1, Output1] = ???
val function2: (Output1) => Either[Failure2, Output2] = ???
def doSomething(s:Id, l:List[Stuff]): Either[InternalError, Output2] = {
val pipeline = function1 andThenE function2
pipeline(InputRequest()) //Why is this of type Either[_, Output2]
}
}
我错过了什么?我如何才能使返回类型不是或[Any,Output2]
,而是基本/密封特征?这是通用的吗?如果您在生产中使用它,并且它不仅仅是一个学习的东西,您正在寻找的东西叫做Kleisli,幸运的是,cats core
已经实现了它
根据cats核心:
Kleisli允许组合返回一元值的函数,
例如,选项[Int]或[String,List[Double]],不带
让函数将选项或其中一个作为参数,可以
奇怪而笨拙
由于Kleisli由两个签名为A=>F[B]
的函数组成,因此您只需要一个抽象就可以使用Kleisli,它正在为您的操作创建一个新类型:
类型操作[A]=任一[InternalFailure,A]
通过这样做,您应该能够像这样使用Kleisli:
导入cats.data.Kleisli
val first:Kleisli[操作,输入请求,输出1]=Kleisli{请求:输入请求=>
左(故障1())
}
val second:Kleisli[操作,输出1,输出2]=Kleisli{output:Output1=>
右(Output2())
}
val组合=第一个。第二个(第二个)
您需要保留左的类型,因此我们将修改扩展方法来实现这一点
请注意,由于两个eithers可以具有不同的左类型,因此我们将使用一个类型绑定来要求编译器推断这些类型之间的LUB;多亏了Any
,这始终是可能的(尽管并非总是有用)
对象扩展{
隐式类和另一个[I,L1,R1](私有值f:I=>其中一个[L1,R1])扩展了AnyVal{
def和thene[L2>:L1,R2](g:R1=>任一[L2,R2]):I=>任一[L2,R2]=
i=>f(i).flatMap(g)
}
}
可以这样使用:
导入EitherExtension_
宾语合成{
密封特性内部误差
最终案例对象失败1扩展内部错误
最终案例对象失败2扩展内部错误
val function1:Int=>[Failure1.type,String]=???
val function2:String=>Failure2.type,布尔值]=???
def doSomething(输入:Int):要么[InternalError,Boolean]={
(功能1和功能2)(输入)
}
}
查看正在运行的代码。好吧,您的和将丢弃左侧的类型。。。所以我不知道你为什么问它为什么这样做,你告诉它这样做。我明白你的意思——但我怎么能让它不这样做呢?和thene
的输出结果总是或[uu,C]
,因为错误可能来自链中的前一个函数,也可能来自此函数,并且两者的类型不同。我不想在泛型函数中声明错误的类型,因此是和签名。@LuisMiguelMejíaSuárez-更新了问题以更好地反映问题的根源。@LuisMiguelMejíaSuárez-请您发表评论/链接,以便我接受它?它确实解决了我的问题,并使编译成功。如果出现新问题,我会问一个新的/后续问题是的,我知道cats
中的Kliesli
结构。你不需要导入一个库就可以运行它。通过强制所有操作[A]
中的任意一个,您已经输入了别名操作[A]
。左侧的类型为内部故障
。我也可以在和中修复它。但这并没有回答问题,而是通过回避我的问题提供了一个解决方案。此外,这样做或通过和进行都是IMHO“生产可行的”。创建Function1
的抽象子类并向其添加和
方法并完成这项工作相对容易-我尝试通过隐式来完成。我以为您只是在寻找一种方法来编写一个返回monad的函数。关于您的实际问题,我不认为仅因为就可以使用flatMap。flatMap
期望A1(或Y,在您的情况下)是X
的超类型,可以与Scala 3+联合类型一起使用,但是: