2012-08-24 30 views
8

के साथ कॉल करता है, मैं जानना चाहता हूं कि किसी भी प्रकार की "विधि कॉल श्रृंखला" बनाना संभव है, सभी विधियों को उसी या तो [त्रुटि, परिणाम] लौटाएं।चेनिंग विधि या तो

मैं क्या करना चाहता हूं: सभी विधियों को क्रमशः कॉल करें, और जब विधि बाएं (त्रुटि) लौटाती है, तो विधि कॉल को रोकें और कॉल श्रृंखला में पाए गए पहले बाएं को वापस करें।

मैंने कुछ सामान, गुना, मानचित्र, अनुमानों के साथ प्रयास किया है ... लेकिन मैं स्कैला के लिए नया हूं और कोई सुरुचिपूर्ण समाधान नहीं ढूंढ रहा हूं।

मुझे लगता है कि जैसे कुछ बात tryed है:

def createUserAndMandatoryCategories(user: User) : Either[Error,User] = { 
    User.create(user).right.map { 
     Logger.info("User created") 
     Category.create(Category.buildRootCategory(user)).right.map { 
     Logger.info("Root category created") 
     Category.create(Category.buildInboxCategory(user)).right.map { 
      Logger.info("Inbox category created") 
      Category.create(Category.buildPeopleCategory(user)).right.map { 
      Logger.info("People category created") 
      Category.create(Category.buildTrashCategory(user)).right.map { 
       Logger.info("Trash category created") 
       Logger.info("All categories successfully created created") 
       Right(user) 
      } 
      } 
     } 
     } 
    } 
    } 

लेकिन यह काम नहीं करता। और वैसे भी मुझे वास्तव में इंडेंटेशन पसंद नहीं है। इसके अलावा मैं (मैं अनुमान मैं का उपयोग गुना चाहिए?)

मैं कुछ इस तरह लिखा तलाश कर रहा हूँ एक नया स्ट्रिंग समस्या का वर्णन में त्रुटि को बदलने के लिए करना चाहते हैं:

val result : Either[String,CallResult] = call1.something("error 1 description") 
.call2.something("error 2 description") 
.call3.something("error 3 description") 
.call4.something("error 4 description") 

क्या यह संभव है स्कैला के साथ ऐसी चीज करने के लिए? शायद दोनों या तो विकल्प का उपयोग कर?

एक बाधा यह भी होगी कि यदि पहली कॉल विफल हो जाती है, तो अन्य कॉल नहीं किए जाने चाहिए। मैं एक समाधान नहीं चाहता जहां मैं सब कुछ कहूं और फिर ईथर में शामिल हो।

धन्यवाद!

+0

हाय, scalaz से विशेषता मान्यता है कि तुम क्या – fp4me

+1

इस के लिए देख रहे हैं या तो इकाई का वर्णन है। –

उत्तर

10

वहाँ बेहतर, अधिक कार्यात्मक यह करने के लिए (ज्यादातर Scalaz के सत्यापन और पार/अनुक्रम शामिल) तरीके हैं, लेकिन अपने कोड के लिए मोटे तौर पर बराबर है:

def createUserAndMandatoryCategories(user: User) : Either[Error,User] = for { 
    _ <- User.create(user).right.map(Logger.info("User created")).right 
    _ <- Category.create(Category.buildRootCategory(user)).right.map(Logger.info("Root category created")).right 
    _ <- Category.create(Category.buildInboxCategory(user)).right.map(Logger.info("Inbox category created")).right 
} yield user 

कौन सा कम से कम सभी घोंसले से छुटकारा मिलता है। चूंकि स्कैला के Either डिफ़ॉल्ट रूप से दाएं पक्षपातपूर्ण नहीं हैं, इसलिए आपको मैन्युअल रूप से कुछ बार निर्दिष्ट करना होगा, जो पठनीयता को थोड़ा कम कर देता है।

+1

+1 सही पूर्वाग्रह पर अच्छा है, डिफ़ॉल्ट रूप से सही पूर्वाग्रह के बारे में स्कैला उपयोगकर्ता (या स्कैला बहस) पर एक थ्रेड देखा। मुझे लगता है कि अब हमें अपने स्वयं के दाएं पूर्वाग्रहों को रोल करना होगा, या जो कुछ भी स्कालज़ अपने नवीनतम और महानतम – virtualeyes

+1

में पकाया गया है, उसके साथ जाना होगा स्कालज़ '\/'प्राप्त करने जा रहा है (हाँ, यह एक टाइप टाइप नहीं है) जो कि है एक सही पक्षपातपूर्ण विकल्प होना चाहिए। – Debilski

+0

धन्यवाद इस तरह से प्रयास करेगा, ऐसा लगता है कि मैं –

2

Debilski कार्यात्मक जा रहा में "" इस सवाल का जवाब है, लेकिन मैं आगे इसे नीचे ट्रिम था कुछ सहायक कोड के साथ:

// trait PackageBase (applicable package objects extend) 
/* 
* not used in this example but can use below implicit to do something like: 
* for { x <- eitherResult as json } 
*/ 
class RightBiasedEither[A,B](e: Either[A,B]) { 
    def as[A1](f: A => A1) = e match { 
    case Left(l) => Left(f(l)).right 
    case Right(r) => Right(r).right 
    } 
} 
@inline implicit final def either2Projection[L,R](e: Either[L,R]) = new RightBiasedEither(e) 

class Catching[T](f: => T) extends grizzled.slf4j.Logging { 
    def either(msg: String) = { // add your own logging here 
    try { Right(f).right } 
    catch { case e: Exception => error(e.getMessage); Left(msg).right } 
    } 
} 
def catching[T](f: => T) = new Catching(f) 

// in your query wrapper equivalent 
protected def either[T](result: => T, msg: String)(implicit ss: Session) = { 
    catching(result) either(msg) 
} 

// and then your DAO create methods will do something like: 
def create(foo: Foo)(implicit ss: Session) { 
    either[Int](Foos.insert(foo), i18n("not created")) 
} 

// with the above code you can then strip things down to: 
def createUserAndMandatoryCategories(user: User) : Either[Error,User] = { 
    db.handle withSession { implicit ss: Session => 
    ss.withTransaction { 
     val result = for { 
     _ <- User.create(user) 
     _ <- Category.create(Category.buildRootCategory(user)) 
     _ <- Category.create(Category.buildInboxCategory(user)) 
     } yield user 
     result fold (e => { ss.rollback; Left(e) }, u => Right(u)) 
    } 
    } 
} 

मेरी ले में सफल निर्माण की घटनाओं (केवल विफलताओं लॉग इन करने की कोई जरूरत नहीं है) चूंकि पूरा लेनदेन विफलता पर वापस लुढ़का है, लेकिन वाईएमएमवी, जैसे आप चाहें लॉगिंग में जोड़ते हैं।

+0

धन्यवाद यह जांच करेगा लेकिन मुझे आपके कोड की सबकुछ समझ में नहीं आती है :)। बीटीडब्ल्यू मुझे लॉगिंग की आवश्यकता नहीं है, बस इसे जोड़ा गया क्योंकि मैं कॉल चेन को एक उचित त्रुटि संदेश –

+0

वापस नहीं कर सका क्योंकि हर किसी ने इंगित किया है, आपको {...} के एक सेट के माध्यम से किसी भी प्रक्षेपण की आवश्यकता है संगणना। यह करने का यह एक तरीका है। सभी क्वेरी अंतर्निहित सत्र सामग्री ScalaQuery, बीटीडब्ल्यू है। इस उदाहरण के लिए आपको अंतर्निहित राइटबाइज्ड क्लास की आवश्यकता नहीं है, लेकिन यदि आप बाएं त्रुटि की स्थिति को एन्कोड करना चाहते हैं तो "वाइस परिणाम = {x <- या तो जेसन के रूप में रीसेट करें}" – virtualeyes

5

RightProjection आप पहले से ही उपयोग कर रहे हैं जो आपको flatMap विधि का उपयोग करने की ज़रूरत है।

(परंपरा के मुताबिक, परिकलन परिणाम Right और त्रुटि मूल्यों में विफल रहा है संगणना के लिए Left में संग्रहीत हैं। लेकिन वहाँ कोई अन्य कारण है, तो आप LeftProjection साथ भी ऐसा ही हो सकता है।)

वास्तव में, हम यहाँ है कि RightProjection एक मोनड बनाता है। आप Right(x).right का उपयोग कर प्रक्षेपण में x मान को परिवर्तित कर सकते हैं। और यदि आपके पास p प्रक्षेपण है, तो आप पर कॉल करके p पर संभावित रूप से असफल गणना f लागू कर सकते हैं। इस तरह, आप कई ऐसी विधियों को श्रृंखलाबद्ध कर सकते हैं।

इसे for समझों द्वारा और अधिक सरल बनाया जा सकता है।एक पूरा उदाहरण के लिए:

object EitherTest extends App { 
    // we define some methods that can either fail 
    // and return a String description of the error, 
    // or return a value 

    def sqrt(x: Double): Either[String,Double] = 
    if (x >= 0) Right(math.sqrt(x)); 
    else Left("Negative value " + x + " cannot be square-rooted."); 

    // or you could have, if you want to avoid typing .right inside `for` later 
    def sqrt0(x: Double): Either.RightProjection[String,Double] = 
    (if (x >= 0) Right(math.sqrt(x)); 
     else Left("Negative value " + x + " cannot be square-rooted.") 
    ).right; 

    def asin(x: Double): Either[String,Double] = 
    if (x > 1) Left("Too high for asin") 
    else if (x < -1) Left("Too low for asin") 
    else Right(math.asin(x)); 


    // Now we try to chain some computations. 
    // In particular, we'll be computing sqrt(asin(x)). 
    // If one of them fails, the rest will be skipped 
    // and the error of the failing one will be returned 
    // as Left. 

    { // try some computations 
    for(i <- -5 to 5) { 
     val input: Double = i/4.0; 
     val d: Either[String,Double] = Right(input); 
     val result: Either[String,Double] = 
     for(v <- d.right; 
      r1 <- asin(v).right; 
      r2 <- sqrt(r1).right 
      // or you could use: 
      // r2 <- sqrt0(r1) 
     ) yield r2; 
     println(input + "\t->\t" + result); 
    } 
    } 
} 

और उत्पादन होता है:

-1.25  ->  Left(Too low for asin) 
-1.0  ->  Left(Negative value -1.5707963267948966 cannot be square-rooted.) 
-0.75  ->  Left(Negative value -0.848062078981481 cannot be square-rooted.) 
-0.5  ->  Left(Negative value -0.5235987755982989 cannot be square-rooted.) 
-0.25  ->  Left(Negative value -0.25268025514207865 cannot be square-rooted.) 
0.0   ->  Right(0.0) 
0.25  ->  Right(0.5026731096270007) 
0.5   ->  Right(0.7236012545582677) 
0.75  ->  Right(0.9209028607738609) 
1.0   ->  Right(1.2533141373155001) 
1.25  ->  Left(Too high for asin) 
+0

धन्यवाद, बहुत अच्छा जवाब! –

संबंधित मुद्दे