2013-05-29 17 views
8

मैं कभी कभी मारा कोड विभाजित करने के लिए कैसे:एफ [एक /बी] में (एफ [एक], एफ [बी]) इस तरह

val things : List[A \/ B] = ??? 
val (as, bs) : (List[A], List[B]) = ??? //insert something to do this 

या मेरे वर्तमान मामले में मैं चाहता हूँ Map[A, B \/ C] => (Map[A, B], Map[A, C])

क्या एफ पर उचित प्रतिबंध के साथ सामान्य मामले F[A \/ B] में ऐसा करने का कोई अच्छा तरीका है? यह अनजिप के विषय पर भिन्नता की तरह अस्पष्ट दिखता है।

उत्तर

9

यहां बताया गया है कि हम इसके साथ/लेकिन किसी भी और प्रमाणीकरण के लिए, न केवल सूचियों के लिए, बल्कि अन्य Foldable के साथ कैसे निपटते हैं।

object Uncozip { 
    implicit val wtf = language.higherKinds 

    // Typeclass which covers sum types such as \/, Either, Validation 
    trait Sum2[F[_, _]] { 
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: F[A, B]): X 
    } 

    implicit val sumEither: Sum2[Either] = new Sum2[Either] { 
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: Either[A, B]): X = { 
     fab match { 
     case Left(l) ⇒ a(l) 
     case Right(r) ⇒ b(r) 
     } 
    } 
    } 

    implicit val sumEitherz: Sum2[\/] = new Sum2[\/] { 
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A \/ B): X = { 
     fab.fold(a(_), b(_)) 
    } 
    } 

    implicit val sumValidation: Sum2[Validation] = new Sum2[Validation] { 
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A Validation B): X = { 
     fab.fold(a(_), b(_)) 
    } 
    } 

    abstract class Uncozips[F[_], G[_, _], A, B](fab: F[G[A, B]]) { 
    def uncozip: (F[A], F[B]) 
    } 

    implicit def uncozip[F[_]: Foldable, G[_, _], A, B](fab: F[G[A, B]])(implicit g: Sum2[G], mfa: ApplicativePlus[F], mfb: ApplicativePlus[F]): Uncozips[F, G, A, B] = new Uncozips[F, G, A, B](fab) { 
    def uncozip = { 
     implicitly[Foldable[F]].foldRight[G[A, B], (F[A], F[B])](fab, (mfa.empty, mfb.empty)) { (l, r) ⇒ 
     g.cata[A, B, (F[A], F[B])]({ (a: A) ⇒ (mfa.plus(mfa.point(a), r._1), r._2) }, 
      { (b: B) ⇒ (r._1, mfa.plus(mfa.point(b), r._2)) })(l) 
     } 
    } 
    } 
} 
+0

आसानी से मानचित्र मामले को कवर नहीं करता है, क्योंकि हम मानचित्र को एक इटरटेबल [(ए, बी \/सी) के रूप में देखते हैं और फिर उसे ए और बी \/सी में विभाजित करते हैं, लेकिन फिर भी बहुत उपयोगी होते हैं। – AlecZorab

2

आप (Option[A], Option[B]) की एक सूची में things नक्शा दो सूचियों में उस सूची अनज़िप, और फिर एकजुट जिसके परिणामस्वरूप सूचियों कर सकते हैं:

import scalaz._ 
import Scalaz._ 

val things: List[String \/ Int] = List("foo".left, 42.right) 
val (strs, ints): (List[String], List[Int]) = things. 
    map { d => (d.swap.toOption, d.toOption) }. // List[(Option[String], Option[Int])] 
    unzip.          // (List[Option[String]], List[Option[Int]]) 
    bimap(_.unite, _.unite)      // (List[String], List[Int]) 

इस वजह से सूची traversing तीन बार विशेष रूप से कुशल नहीं है ।

1

यहाँ एक ही रास्ता (सूचियों के लिए) है:

val things : List[A \/ B] = ??? 
val (as, bs) = (things.map(_.swap.toList).join, things.map(_.toList).join) 

और नक्शे के लिए:

val things: Map[String, String \/ Int] = ??? 
val (as, bs) = (things.mapValues(_.swap.toList).filterNot(e => e._2.isEmpty), 
       things.mapValues(_.toList).filterNot(e => e._2.isEmpty)) 

मैं एक तरह से इस सामान्यीकरण करने के लिए साथ आ एक कठिन समय हो रही है F पर (मुझे विश्वास है कि आपको और ApplicativeF के लिए उदाहरणों की आवश्यकता होगी)।