scala - समझ और monads के साथ, स्कैला में विधि पैरामीटर सत्यापन




for-comprehension either (3)

मैं मूर्खता के लिए एक विधि के पैरामीटर को प्रमाणित करने की कोशिश कर रहा हूं लेकिन मुझे समाधान नहीं मिला है ...

क्या कोई मुझे बता सकता है कि कैसे करना है?

मैं इस तरह कुछ कोशिश कर रहा हूँ:

  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) )
    }
  }

मान लीजिए कि आपने निम्न त्वरित और गंदे सामानों के साथ या तो पूरा कर लिया है:

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)

एक ट्रीपल लौटने वाला एक करीबी कन्स्ट्रक्टर:

  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 )
   ))

कोई बड़ी बात नहीं।


मैं नल-उत्पादक एपीआई के लिए एक रैपर बनाने के लिए बेन जेम्स के सुझाव का पूरी तरह से समर्थन करता हूं। लेकिन उस रैपर को लिखते समय भी आपको वही समस्या होगी। तो यहां मेरे सुझाव हैं।

समझदारी के लिए क्यों monads? एक अधीरता आईएमओ। यहां बताया गया है कि आप यह कैसे कर सकते हैं:

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(", ")
      )
    )
  }

यदि आपको @ ट्रेविस ब्राउन के उत्तर के आवेदक मज़ेदार दृष्टिकोण पसंद हैं, लेकिन आपको स्कालाज़ सिंटैक्स पसंद नहीं है या अन्यथा स्कालाज़ का उपयोग नहीं करना चाहते हैं, तो यहां एक साधारण लाइब्रेरी है जो मानक पुस्तकालय को समृद्ध करती है या तो कक्षा को आवेदक के रूप में कार्य करने के लिए मज़ेदार सत्यापन: 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)))
}

दूसरे शब्दों में, यदि यह एथर्स अधिकार थे, तो यह फ़ंक्शन आपकी श्रेणी वाले अधिकार को वापस कर देगा, या यदि सभी एक या अधिक लीफ थे, तो यह सभी त्रुटियों की एक सूची वाला वाम वापस कर देगा।

तर्कसंगत रूप से अधिक Scala-ish और कम Haskell-ish वाक्यविन्यास, और एक छोटी पुस्तकालय पर ध्यान दें;)





either