链接Scalaz验证函数:函数1[A,验证[E,B]]

链接Scalaz验证函数:函数1[A,验证[E,B]],scala,scalaz,Scala,Scalaz,我正在尝试编写一些代码,以便于链接返回ScalazValidation类型的函数。我尝试编写的一种方法类似于验证.flatMap(短路该验证),我将其称为和pipe。另一个类似于ApplicationBuilder(累积错误)上的@,只是它只返回最后的Success类型,我将调用并传递 假设我有函数: def allDigits: (String) => ValidationNEL[String, String] def maxSizeOfTen: (String) => Valid

我正在尝试编写一些代码,以便于链接返回Scalaz
Validation
类型的函数。我尝试编写的一种方法类似于
验证.flatMap
(短路该验证),我将其称为
和pipe
。另一个类似于
ApplicationBuilder
(累积错误)上的
@
,只是它只返回最后的
Success
类型,我将调用
并传递

假设我有函数:

def allDigits: (String) => ValidationNEL[String, String]
def maxSizeOfTen: (String) => ValidationNEL[String, String] 
def toInt: (String) => ValidationNEL[String, Int]
例如,我想首先将输入字符串传递给allDigits和maxSizeOf10。如果出现故障,它应该通过不调用toInt函数来短路,并返回发生的一个或两个故障。如果成功,我希望将成功值传递给toInt函数。从这里开始,它要么在输出值为Int的情况下成功,要么只从toInt返回验证失败

def intInput: (String) => ValidationNEL[String,Int] = (allDigits andPass maxSizeOfTen) andPipe toInt 
有没有一种方法可以在不使用下面的附加组件实现的情况下实现这一点

以下是我的实现:

  trait ValidationFuncPimp[E,A,B] {
    val f: (A) => Validation[E, B]

    /** If this validation passes, pass to f2, otherwise fail without accumulating. */
    def andPipe[C](f2: (B) => Validation[E,C]): (A) => Validation[E,C] = (a: A) => {
      f(a) match {
        case Success(x) => f2(x)
        case Failure(x) => Failure(x)
      }
    }

    /** Run this validation and the other validation, Success only if both are successful.  Fail accumulating errors. */
    def andPass[D](f2: (A) => Validation[E,D])(implicit S: Semigroup[E]): (A) => Validation[E,D] = (a:A) => {
      (f(a), f2(a)) match {
        case (Success(x), Success(y)) => Success(y)
        case (Failure(x), Success(y)) => Failure(x)
        case (Success(x), Failure(y)) => Failure(y)
        case (Failure(x), Failure(y)) => Failure(S.append(x, y))
      }
    }
  }
  implicit def toValidationFuncPimp[E,A,B](valFunc : (A) => Validation[E,B]): ValidationFuncPimp[E,A,B] = {
    new ValidationFuncPimp[E,A,B] {
      val f = valFunc
    }
  }

这相对简洁,几乎没有“添加的代码”。但它仍然有点不可靠,因为它忽略了应用
allDigits
的成功结果

scala> val validated = for {
     |   x <- allDigits
     |   y <- maxSizeOfTen
     | } yield x *> y
validated: String => scalaz.Validation[scalaz.NonEmptyList[String],String] = <function1>

scala> val validatedToInt = (str: String) => validated(str) flatMap(toInt)
validatedToInt: String => scalaz.Validation[scalaz.NonEmptyList[String],Int] = <function1>

scala> validatedToInt("10")
res25: scalaz.Validation[scalaz.NonEmptyList[String],Int] = Success(10)
我很好奇是否有人能想出一个更好的方法来组合这些。这不是真正的作文

val validatedToInt = (str: String) => validated2(str) flatMap(_ => toInt(str))
validated
validated2
累积故障,如下所示:

scala> def allDigits: (String) => ValidationNEL[String, String] = _ => failure(NonEmptyList("All Digits Fail"))
allDigits: String => scalaz.Scalaz.ValidationNEL[String,String]

scala> def maxSizeOfTen: (String) => ValidationNEL[String, String] = _ => failure(NonEmptyList("max > 10"))
maxSizeOfTen: String => scalaz.Scalaz.ValidationNEL[String,String]

scala> val validated = for {
     |   x <- allDigits
     |   y <- maxSizeOfTen
     | } yield x *> y
validated: String => scalaz.Validation[scalaz.NonEmptyList[String],String] = <function1>

scala> val validated2 = for {
     |   x <- allDigits
     |   y <- maxSizeOfTen
     | } yield x <|*|> y
validated2: String => scalaz.Validation[scalaz.NonEmptyList[String],(String, String)] = <function1>

scala> validated("ten")
res1: scalaz.Validation[scalaz.NonEmptyList[String],String] = Failure(NonEmptyList(All Digits Fail, max > 10))

scala> validated2("ten")
res3: scalaz.Validation[scalaz.NonEmptyList[String],(String, String)] = Failure(NonEmptyList(All Digits Fail, max > 10))
scala>def allDigits:(字符串)=>ValidationNEL[String,String]=\u=>失败(非空列表(“所有数字失败”))
allDigits:String=>scalaz.scalaz.ValidationNEL[String,String]
scala>def maxSizeOfTen:(字符串)=>ValidationNEL[String,String]=\u=>失败(非空列表(“最大值>10”))
maxSizeOfTen:String=>scalaz.scalaz.ValidationNEL[String,String]
scala>val validated=for{
|x scalaz.Validation[scalaz.NonEmptyList[String],String]=
scala>val validated2=for{
|十(十)
res1:scalaz.Validation[scalaz.NonEmptyList[String],String]=失败(NonEmptyList(所有数字都失败,最大值>10))
scala>validated2(“十”)
res3:scalaz.Validation[scalaz.NonEmptyList[String],(String,String)]=失败(NonEmptyList(所有数字都失败,最大值>10))

将ApplicationBuilder与前两个一起使用,以累积错误, 然后flatMap
toInt
,因此只有在前两个成功的情况下才会调用
toInt

val validInt: String => ValidationNEL[String, Int] = 
  for {
    validStr <- (allDigits |@| maxSizeOfTen)((x,_) => x); 
    i <- toInt
  } yield(i)
val validInt:String=>ValidationNEL[String,Int]=
为了{
validStr x);

我并不是说这个答案一定比我的好,但它采用了一种稍微不同的方法,不适合在这里发表评论

首先是我们的验证方法(请注意,我将
的类型更改为int
,原因我将在下面解释):

现在我们有两支Kleisli箭:

val validator = Kleisli[ErrorsOr, String, String](
  allDigits.flatMap(x => maxSizeOfTen.map(x *> _)) andThen (_.disjunction)
)

val integerizer = Kleisli[ErrorsOr, String, Int](toInt)
我们可以组成:

val together = validator >>> integerizer
然后像这样使用:

scala> together("aaa")
res0: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits))

scala> together("12345678900")
res1: ErrorsOr[Int] = -\/(NonEmptyList(Too big))

scala> together("12345678900a")
res2: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits, Too big))

scala> together("123456789")
res3: ErrorsOr[Int] = \/-(123456789)

在非一元的东西上使用
flatMap
让我有点不舒服,将我们的两个
ValidationNEL
方法组合成
\/
单子中的Kleisli箭头,它也可以作为字符串到整数转换的合适模型,这让我感觉有点干净。

你想通过成功吗ess值从
allDigits
maxSizeOfTen
并累积失败?或者,您是否试图使用相同的输入调用
allDigits
maxSizeOfTen
toInt
,累积失败并抛出除
toInt
之外的所有成功?我编辑了“作为示例”一段,以使其更加清晰(希望如此)。好吧,这太愚蠢了。我将隐式转换隐藏在trait中,因此它找不到它。然而,问题仍然存在:没有添加的代码,有没有办法做到这一点。添加的代码,你是指你的验证皮条客吗?是的,没有验证皮条客。如果你使用“理解”来尝试组合allDigits和maxSizeOfTen,那么它将我会“快速失败”如果allDigits失败,不要调用maxSizeOfTen。@实际上,这里要理解的是调用
Function1
Functor上的
flatMap
/
map
,而不是验证。在生成之前,根本没有失败。请标记您的答案,解释您的情况,要求将其转换回常规答案。它们是如果这是合法的,那么做很好。+1,但请参阅我的答案,了解一种方法(不一定更好)到合成。@Daenyth
来自scalaz 6.x,在scalaz 7.x中被重命名为
tuple
。它由
scalaz.syntax.apply.apply.提供。您忽略了
validStr
。如果
所有数字
maxSizeOfTen
失败,则仍将调用
toInt
,您将只生成这就是。
所有数字
最大值
toInt
都是
函数1
,而不是
验证
。这就是这个问题的有趣之处。当然,你可以用你现有的来创建一个
函数1
,但我认为OP试图想出一个更好的方法来编写这些函数,而不需要m手动将参数传递给
validInt
到所有其他三个函数。因为我们处理的是(A)=>验证而不是验证,所以这不太有效。只需稍加修改,就可以生成:gist.github.com/4347051@OleTravel是的,这就是我的意思“手动将参数传递给其他三个函数。”
val validator = Kleisli[ErrorsOr, String, String](
  allDigits.flatMap(x => maxSizeOfTen.map(x *> _)) andThen (_.disjunction)
)

val integerizer = Kleisli[ErrorsOr, String, Int](toInt)
val together = validator >>> integerizer
scala> together("aaa")
res0: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits))

scala> together("12345678900")
res1: ErrorsOr[Int] = -\/(NonEmptyList(Too big))

scala> together("12345678900a")
res2: ErrorsOr[Int] = -\/(NonEmptyList(Not all digits, Too big))

scala> together("123456789")
res3: ErrorsOr[Int] = \/-(123456789)