복붙노트

[SCALA] 분리 대 검증

SCALA

분리 대 검증

나는 다음과 같은 서명을 가진 메소드를 작성한다고 가정 :

def parse(input: List[(String, String)]):
  ValidationNel[Throwable, List[(Int, Int)]]

상기 입력 문자열의 각각의 쌍의 경우, 양쪽 부재의 정수로서 상기 제는 초보다 작은 것으로 해석 될 수 있다는 것을 확인해야한다. 그런 다음 올려 오류를 축적, 정수를 반환해야합니다.

우선 오류 유형을 정의 할 수 있습니다 :

import scalaz._, Scalaz._

case class InvalidSizes(x: Int, y: Int) extends Exception(
  s"Error: $x is not smaller than $y!"
)

이제 다음과 같이 나는 나의 방법을 구현할 수 있습니다 :

def checkParses(p: (String, String)):
  ValidationNel[NumberFormatException, (Int, Int)] =
  p.bitraverse[
    ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
  ](
    _.parseInt.toValidationNel,
    _.parseInt.toValidationNel
  )

def checkValues(p: (Int, Int)): Validation[InvalidSizes, (Int, Int)] =
  if (p._1 >= p._2) InvalidSizes(p._1, p._2).failure else p.success

def parse(input: List[(String, String)]):
  ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
    checkParses(p).fold(_.failure, checkValues _ andThen (_.toValidationNel))
  )

또는 대안 :

def checkParses(p: (String, String)):
  NonEmptyList[NumberFormatException] \/ (Int, Int) =
  p.bitraverse[
    ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
  ](
    _.parseInt.toValidationNel,
    _.parseInt.toValidationNel
  ).disjunction

def checkValues(p: (Int, Int)): InvalidSizes \/ (Int, Int) =
  (p._1 >= p._2) either InvalidSizes(p._1, p._2) or p

def parse(input: List[(String, String)]):
  ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
    checkParses(p).flatMap(s => checkValues(s).leftMap(_.wrapNel)).validation
  )

지금은 어떤 이유로 두 번째 (값을 검사)가 분리 문제 같은 느낌 동안 (쌍은 문자열로 구문 분석하는 것을 확인하는) 첫 번째 작업은 검증 문제처럼 나에게 느낌, 나는 (두 monadically를 구성 할 필요가처럼 느낀다 이는 내가) / ValidationNel 있기 때문에 Throwable를, _] 모나드 인스턴스가없는 \ 사용되어야 함을 시사한다.

내 첫 번째 구현, 나는 내내 ValidationNel을 사용하고 가짜의 flatMap의 일종으로 마지막에 접어. 제에서, 난 축적 또는 모나드 바인딩 에러해야하는지에 따라 적절 / 앞뒤로 ValidationNel과 \ 사이 반송. 그들은 동일한 결과를 생성합니다.

나는 실제 코드에서 두 가지 접근 방식을 사용했습니다, 아직 다른 이상 하나에 대한 선호도를 개발하지 않았습니다. 나는 뭔가를 놓치고 있습니까? 나는 하나는 다른 이상을 선호 하는가?

해결법

  1. ==============================

    1.이것은 아마도 당신이 찾고있는 해답이 아니라 그냥 검증은 다음과 같은 방법을 가지고 발견

    이것은 아마도 당신이 찾고있는 해답이 아니라 그냥 검증은 다음과 같은 방법을 가지고 발견

    /** Run a disjunction function and back to validation again. Alias for `@\/` */
    def disjunctioned[EE, AA](k: (E \/ A) => (EE \/ AA)): Validation[EE, AA] =
      k(disjunction).validation
    
    /** Run a disjunction function and back to validation again. Alias for `disjunctioned` */
    def @\/[EE, AA](k: (E \/ A) => (EE \/ AA)): Validation[EE, AA] =
      disjunctioned(k)
    

    내가 그들을 보았을 때 나는이 질문을 기억까지, 정말 자신의 유용성을 볼 수 없었다. 그들은 당신이 분리로 변환하여 적절한 결합을 할 수 있습니다.

    def checkParses(p: (String, String)):
      ValidationNel[NumberFormatException, (Int, Int)] =
      p.bitraverse[
        ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
      ](
        _.parseInt.toValidationNel,
        _.parseInt.toValidationNel
      )
    
    def checkValues(p: (Int, Int)): InvalidSizes \/ (Int, Int) =
      (p._1 >= p._2) either InvalidSizes(p._1, p._2) or p
    
    def parse(input: List[(String, String)]):
      ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
        checkParses(p).@\/(_.flatMap(checkValues(_).leftMap(_.wrapNel)))
      )
    
  2. ==============================

    2.다음은 고양이에 대한 내 코드의 두 번째 버전의 아주 가까이 번역 한 것입니다 :

    다음은 고양이에 대한 내 코드의 두 번째 버전의 아주 가까이 번역 한 것입니다 :

    import scala.util.Try
    
    case class InvalidSizes(x: Int, y: Int) extends Exception(
      s"Error: $x is not smaller than $y!"
    )
    
    def parseInt(input: String): Either[Throwable, Int] = Try(input.toInt).toEither
    
    def checkValues(p: (Int, Int)): Either[InvalidSizes, (Int, Int)] =
      if (p._1 >= p._2) Left(InvalidSizes(p._1, p._2)) else Right(p)
    
    import cats.data.{EitherNel, ValidatedNel}
    import cats.instances.either._
    import cats.instances.list._
    import cats.syntax.apply._
    import cats.syntax.either._
    import cats.syntax.traverse._
    
    def checkParses(p: (String, String)): EitherNel[Throwable, (Int, Int)] =
      (parseInt(p._1).toValidatedNel, parseInt(p._2).toValidatedNel).tupled.toEither
    
    def parse(input: List[(String, String)]): ValidatedNel[Throwable, List[(Int, Int)]] =
      input.traverse(fields =>
        checkParses(fields).flatMap(s => checkValues(s).toEitherNel).toValidated
      )
    

    질문을 업데이트하려면,이 코드는 "나는 바인딩 축적 또는 모나드 에러 필요 여부에 따라 적절하게 앞뒤로 ValidatedNel와 어느 사이에 수신 거부"입니다.

    나는이 질문을 이후 거의 6 년 동안, 고양이는 해결할 수있는 문제가 정확하게 문제를 내가로 실행중인 것을 (고양이 2.0.0 개선) 병렬 형 클래스를 발표했다 :

    import cats.data.EitherNel
    import cats.instances.either._
    import cats.instances.list._
    import cats.instances.parallel._
    import cats.syntax.either._
    import cats.syntax.parallel._
    
    def checkParses(p: (String, String)): EitherNel[Throwable, (Int, Int)] =
      (parseInt(p._1).toEitherNel, parseInt(p._2).toEitherNel).parTupled
    
    def parse(input: List[(String, String)]): EitherNel[Throwable, List[(Int, Int)]] =
      input.parTraverse(fields =>
        checkParses(fields).flatMap(checkValues(_).toEitherNel)
      )
    

    우리는 트래버스처럼 우리의 실용적 사업자의 파 버전을 전환하거나 우리가 축적 오류로 할 때 tupled하지만, 그렇지 않으면 우리 바인딩 모나드 우리를 제공 하나에있어 작업, 우리는 더 이상 전혀 인증 됨를 참조 할 수 있습니다.

  3. from https://stackoverflow.com/questions/20065853/validation-versus-disjunction by cc-by-sa and MIT license