복붙노트

[SCALA] 방법은 이해와 모나드에 대한로, 스칼라 유효성 검사 매개 변수

SCALA

방법은 이해와 모나드에 대한로, 스칼라 유효성 검사 매개 변수

나는 무효위한 방법의 매개 변수의 유효성을 확인하는 데 노력하고있어하지만 난 해결책을 찾을 수없는 ...

사람이 수행하는 방법을 말해 줄래?

나는 이런 식으로 뭔가를 시도하고있다 :

  def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[Error,Category] = {
    val errors: Option[String] = for {
      _ <- Option(user).toRight("User is mandatory for a normal category").right
      _ <- Option(parent).toRight("Parent category is mandatory for a normal category").right
      _ <- Option(name).toRight("Name is mandatory for a normal category").right
      errors : Option[String] <- Option(description).toRight("Description is mandatory for a normal category").left.toOption
    } yield errors
    errors match {
      case Some(errorString) => Left( Error(Error.FORBIDDEN,errorString) )
      case None =>  Right( buildTrashCategory(user) )
    }
  }

해결법

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

    1.당신이 Scalaz를 사용하고자하는 경우, 그것은 새로운 검증 클래스와 평범한 오래된 scala.Either에 대한 몇 가지 유용한 마우스 오른쪽 바이어스 타입의 클래스 인스턴스를 포함하여 작업의 종류보다 편리하게 도구의 소수를 가지고있다. 여기 각각의 예를 줄 것이다.

    당신이 Scalaz를 사용하고자하는 경우, 그것은 새로운 검증 클래스와 평범한 오래된 scala.Either에 대한 몇 가지 유용한 마우스 오른쪽 바이어스 타입의 클래스 인스턴스를 포함하여 작업의 종류보다 편리하게 도구의 소수를 가지고있다. 여기 각각의 예를 줄 것이다.

    먼저 우리 Scalaz 수입에 대한 (주 우리는 이름 충돌을 피하기 위해 scalaz.Category를 숨길 가질 수) :

    import scalaz.{ Category => _, _ }
    import syntax.apply._, syntax.std.option._, syntax.validation._
    

    나는이 예를 들어 Scalaz 7을 사용하고 있습니다. (6)을 사용하는 당신은 몇 가지 사소한 변경해야하는 것입니다.

    나는 우리가이 단순화 된 모델을 가정합니다 :

    case class User(name: String)
    case class Category(user: User, parent: Category, name: String, desc: String)
    

    다음으로는 null 값을 검사 포함하지 않는 방식으로 사용하면 쉽게 적응할 수있는 다음과 같은 검증 방법을 정의 할 수 있습니다 :

    def nonNull[A](a: A, msg: String): ValidationNel[String, A] =
       Option(a).toSuccess(msg).toValidationNel
    

    Nel 보낸 부분은 "비어 있지 않은 목록"을 의미하고, ValidationNel [문자열, A는] 본질적으로 어느 [목록 [문자열], A]와 동일합니다.

    이제 우리는 우리의 주장을 확인하기 위해이 방법을 사용 :

    def buildCategory(user: User, parent: Category, name: String, desc: String) = (
      nonNull(user,   "User is mandatory for a normal category")            |@|
      nonNull(parent, "Parent category is mandatory for a normal category") |@|
      nonNull(name,   "Name is mandatory for a normal category")            |@|
      nonNull(desc,   "Description is mandatory for a normal category")
    )(Category.apply)
    

    검증 [무엇이든간에, _이 (이유 예를 들어, 여기서 논의에 대한) 모나드가되지 않습니다,하지만 ValidationNel [문자열, _]은 실용적 펑, 그리고 우리가 때 "리프트"카테고리 여기에 그 사실을 사용하고 있습니다. 그것으로 적용됩니다. 실용적 펑에 대한 자세한 내용은 아래의 부록을 참조하십시오.

    우리는 다음처럼 작성할 경우 현재 :

    val result: ValidationNel[String, Category] = 
      buildCategory(User("mary"), null, null, "Some category.")
    

    우리는 축적 된 오류와 실패를 얻을 수 있습니다 :

    Failure(
     NonEmptyList(
       Parent category is mandatory for a normal category,
       Name is mandatory for a normal category
      )
    )
    

    모든 인수는 체크 아웃 한 경우에, 우리는 대신 범주 값을 가진 성공이있을 것이다.

    확인을 위해 실용적 펑 사용에 대한 편리한 것 중 하나는 당신이 오류를 처리에 당신의 접근 방식을 바꿀 수있는 용이성이다. 대신 그들을 축적의 첫 번째에 실패 할 경우, 당신은 기본적으로 당신의 널 (null)이 아닌 방법을 변경할 수 있습니다.

    우리는 수입의 약간 다른 설정이 필요합니까 :

    import scalaz.{ Category => _, _ }
    import syntax.apply._, std.either._
    

    그러나 위의 경우 클래스를 변경할 필요가 없습니다.

    여기에 우리의 새로운 검증 방법입니다 :

    def nonNull[A](a: A, msg: String): Either[String, A] = Option(a).toRight(msg)
    

    우리가 어느 대신 ValidationNEL으로 사용하는 것을 제외하고는 위의 일, 그리고 어느 오류를 축적하지 않습니다에 대한 Scalaz가 제공하는 기본 실용적 펑터 인스턴스 거의 동일합니다.

    그게 우리가 원하는 르파 행동에는 변화가 우리의 buildCategory 방법에 필요하지 않습니다 얻기 위해해야 ​​할 전부입니다. 이제 우리는이를 작성하는 경우 :

    val result: Either[String, Category] =
      buildCategory(User("mary"), null, null, "Some category.")
    

    결과는 첫 번째 오류를 포함합니다 :

    Left(Parent category is mandatory for a normal category)
    

    정확히 우리가 원하는대로.

    우리는 하나의 인수를하는 방법이 있다고 가정 :

    def incremented(i: Int): Int = i + 1
    

    옵션 [지능] 다시 옵션 [지능]을 얻을 : 그리고 우리는 몇 가지 X에이 방법을 적용 할 것을 또한 가정합니다. 옵션 펑터이며, 따라서지도 방법을 제공한다는 사실이 쉽게 :

    val xi = x map incremented
    

    우리는 "해제"옵션 펑터로 증가했습니다; 즉, 우리는 기본적으로 옵션에 하나 개의 매핑 옵션 [지능]에 int로 함수 맵핑의 지능을 변경 한 [Int 인은 (구문이 그를 muddies하지만 조금-은 "리프팅"은유는 하스켈과 같은 언어로 훨씬 명확) .

    이제 우리는 비슷한 방식으로 x와 y에 메소드를 추가, 다음이 적용한다고 가정합니다.

    def add(i: Int, j: Int): Int = i + j
    
    val x: Option[Int] = users.find(_.name == "John").map(_.age)
    val y: Option[Int] = users.find(_.name == "Mary").map(_.age) // Or whatever.
    

    옵션 펑터가 있다는 사실은 충분하지 않습니다. 그것은, 그러나, 모나드 있다는 사실은, 우리는 우리가 원하는 것을 얻을 flatMap을 사용할 수 있습니다 :

    val xy: Option[Int] = x.flatMap(xv => y.map(add(xv, _)))
    

    또는, 동등 :

    val xy: Option[Int] = for { xv <- x; yv <- y } yield add(xv, yv)
    

    어떤 의미에서,하지만 옵션의 monadness이 작업에 대한 과잉이다. 간단한 추상화 소위가있다 실용적 펑 - 그건-의 사이에 펑하고 모나드와 우리가 필요로하는 것을 제공하는 모든 기계.

    년 사이에 공식적인 의미에서 그것의 참고 : 모든 모나드는 실용적 펑은, 모든 실용적 펑은 펑 아니지만 모든 실용적 펑은 모나드 등이다

    Scalaz 우리에게 옵션에 대한 실용적 펑 인스턴스를 제공합니다, 그래서 우리는 다음과 같이 쓸 수 있습니다 :

    import scalaz._, std.option._, syntax.apply._
    
    val xy = (x |@| y)(add)
    

    구문은 약간 이상한이지만 개념은 전술 로지텍은 단지 실용적으로 펑 추가 리프팅 펑 또는 모나드 예보다 더 복잡하지 않다. 우리는 세 개의 인수 메소드 (F)이 있다면, 우리는 다음과 같이 쓸 수있다 :

    val xyz = (x |@| y |@| z)(f)
    

    등등.

    우리는 모나드를 가지고 그래서 왜 전혀 실용적 펑 귀찮게? 우선, 우리가 유효성 검사와-완벽한 예입니다 작업 할 추상화의 일부 모나드 인스턴스를 제공하기 위해 단순히 불가능합니다.

    두 번째 (그리고 이와 관련하여), 그것은 일을 얻을 것이다 가장 강력한 추상화를 사용하는 단지 고체 개발 좋습니다. 원칙적으로이 그렇지 않으면 불가능했을 최적화를 허용 할 수 있지만, 더 중요한 것은 우리가 더 재사용 작성한 코드를 만든다.

  2. ==============================

    2.나는 완전히 널 생산 API에 대한 래퍼를 만들기 위해 벤 제임스의 제안을 지원합니다. 그 래퍼를 작성하지만 당신은 여전히 ​​같은 문제가 있습니다. 그래서 여기 내 제안이다.

    나는 완전히 널 생산 API에 대한 래퍼를 만들기 위해 벤 제임스의 제안을 지원합니다. 그 래퍼를 작성하지만 당신은 여전히 ​​같은 문제가 있습니다. 그래서 여기 내 제안이다.

    왜 모나드 이유를 이해 하시나요? IMO overcomplication. 여기에 당신이 할 수있는 방법은 다음과 같습니다

    def buildNormalCategory
      ( user: User, parent: Category, name: String, description: String )
      : Either[ Error, Category ] 
      = Either.cond( 
          !Seq(user, parent, name, description).contains(null), 
          buildTrashCategory(user),
          Error(Error.FORBIDDEN, "null detected")
        )
    

    또는 오류 메시지가 매개 변수의 이름을 저장 가진 고집, 당신은 좀 더 상용구를 필요로 다음을 할 수 있습니다 :

    def buildNormalCategory
      ( user: User, parent: Category, name: String, description: String )
      : Either[ Error, Category ] 
      = {
        val nullParams
          = Seq("user" -> user, "parent" -> parent, 
                "name" -> name, "description" -> description)
              .collect{ case (n, null) => n }
    
        Either.cond( 
          nullParams.isEmpty, 
          buildTrashCategory(user),
          Error(
            Error.FORBIDDEN, 
            "Null provided for the following parameters: " + 
            nullParams.mkString(", ")
          )
        )
      }
    
  3. ==============================

    3.당신이 @Travis 브라운의 대답의 실용적 펑터 접근 방식을 좋아하지만 경우에는 Scalaz 구문을 싫어하거나 단지 Scalaz을 사용하지 않으려는, 여기에 표준 라이브러리 어느 클래스를 풍요롭게 간단한 라이브러리가 실용적 역할을하는 것입니다 펑 검증 : https://github.com/youdevise/eithervalidation

    당신이 @Travis 브라운의 대답의 실용적 펑터 접근 방식을 좋아하지만 경우에는 Scalaz 구문을 싫어하거나 단지 Scalaz을 사용하지 않으려는, 여기에 표준 라이브러리 어느 클래스를 풍요롭게 간단한 라이브러리가 실용적 역할을하는 것입니다 펑 검증 : https://github.com/youdevise/eithervalidation

    예를 들면 :

    import com.youdevise.eithervalidation.EitherValidation.Implicits._    
    
    def buildNormalCategory(user: User, parent: Category, name: String, description: String): Either[List[Error], Category] = {     
      val validUser = Option(user).toRight(List("User is mandatory for a normal category"))
      val validParent = Option(parent).toRight(List("Parent category is mandatory for a normal category"))
      val validName = Option(name).toRight(List("Name is mandatory for a normal category"))
      Right(Category)(validUser, validParent, validName).
        left.map(_.map(errorString => Error(Error.FORBIDDEN, errorString)))
    }
    

    다시 말해서 Eithers의 모든 권리라면,이 기능은 사용자의 범주를 포함하는 오른쪽을 반환합니다, 또는 하나 이상의 좌파을했다 경우는 왼쪽 모든 오류의 목록을 포함하는 반환합니다.

    틀림없이 더 스칼라 틱 덜 하스켈 틱 구문 및 작은 도서관을 주목하라)

  4. ==============================

    4.다음 신속하고 더러운 물건 중 하나를 완료했다고 가정하자 :

    다음 신속하고 더러운 물건 중 하나를 완료했다고 가정하자 :

    object Validation {
      var errors = List[String]()  
    
      implicit class Either2[X] (x: Either[String,X]){
    
    def fmap[Y](f: X => Y) = {
      errors = List[String]()  
      //println(s"errors are $errors")
      x match {
        case Left(s) => {errors = s :: errors ; Left(errors)}
        case Right(x) => Right(f(x))
      }
    }    
    def fapply[Y](f: Either[List[String],X=>Y]) = {
      x match { 
        case Left(s) => {errors = s :: errors ; Left(errors)}
        case Right(v) => {
          if (f.isLeft) Left(errors) else Right(f.right.get(v))
        }
      }
    }
    }}
    

    양자 택일을 반환 유효성 검사 기능을 고려 :

      def whenNone (value: Option[String],msg:String): Either[String,String] = 
          if (value isEmpty) Left(msg) else Right(value.get)
    

    는 A curryfied 생성자는 튜플을 반환 :

      val me = ((user:String,parent:String,name:String)=> (user,parent,name)) curried
    

    당신은 그것을과 유효성을 검사 할 수 있습니다 :

       whenNone(None,"bad user") 
       .fapply(
       whenNone(Some("parent"), "bad parent") 
       .fapply(
       whenNone(None,"bad name") 
       .fmap(me )
       ))
    

    아니 큰 문제.

  5. from https://stackoverflow.com/questions/12307965/method-parameters-validation-in-scala-with-for-comprehension-and-monads by cc-by-sa and MIT license