Scala Programming Language
  1. Scala Programming Language
  2. SI-5793

definition inside a for comprehension based on an Either fails to compile

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Not a Bug
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Misc Compiler, Misc Library
    • Labels:
      None
    • Environment:

      scala 2.9.2 and 2.10.0-M3

      Description

      Including a definition in a for comprehension works if the comprehension is using Option, but the compiler complains about a for comprehension with Either (RightProjection).

      Using for comprehensions makes for very readable code, and flowing though errors via Either seems like it ought to be the right thing to do.

      Example:

      def opt:Option[Char] = Some('e')
      def f(c:Char):String = "ha"
      def either:Either[Int,Char] = Right('r')
      
      val optComprehensionWithAssignment:Option[Int] =
        for {
          c <- opt
          x = c.toInt
        } yield x
      
      val eitherComprehensionNoAssignment:Either[Int,String] = 
        for { 
          c <- either.right
        } yield {
          val s = f(c)
          s
        }
      
      val eitherComprehensionWithAssighment:Either[Int,String] = 
        for { 
          c <- either.right
          s = f(c)                // doesn't compile
        } yield s
      
      
      val workaround:Either[Int,String] = 
        for { 
          c <- either.right
          s <- Right(f(c)).right       // workaround's a bit awkward
        } yield s
      
      

        Activity

        Hide
        Jason Zaugg added a comment -

        You are essentially trying to call:

        scala> either.right.map(x => x).map(x => x)
        <console>:9: error: value map is not a member of Either[Int,Char] with Product with Serializable
                      either.right.map(x => x).map(x => x)
                                               ^
        
        

        But Either#RightProjection#map returns an Either, which doesn't have a map method.

        You can enrich Either to have a right-biased set of map/flatMap/foreach methods, along these lines (I'm using Scala 2.10 implicit classes for brevity, but the same is possible in 2.9.x with a class and an implicit conversion):

        scala> implicit class RightBiasedEither[A, B](val e: Either[A, B]) extends AnyVal {
             |   def foreach[U](f: B => U): Unit = e.right.foreach(f)
             |   def map[C](f: B => C): Either[A, C] = e.right.map(f)
             |   def flatMap[C](f: B => Either[A, C]) = e.right.flatMap(f)  
             | }
        defined class RightBiasedEither
        
        scala> 
        
        scala> def f(c:Char):String = "ha"
        f: (c: Char)String
        
        scala> def either:Either[Int,Char] = Right('r')
        either: Either[Int,Char]
        
        scala>   for { 
             |     c <- either
             |     s = f(c)
             |   } yield s
        res0: Either[Int,String] = Right(ha)
        
        Show
        Jason Zaugg added a comment - You are essentially trying to call: scala> either.right.map(x => x).map(x => x) <console>:9: error: value map is not a member of Either[Int,Char] with Product with Serializable either.right.map(x => x).map(x => x) ^ But Either#RightProjection#map returns an Either , which doesn't have a map method. You can enrich Either to have a right-biased set of map/flatMap/foreach methods, along these lines (I'm using Scala 2.10 implicit classes for brevity, but the same is possible in 2.9.x with a class and an implicit conversion): scala> implicit class RightBiasedEither[A, B](val e: Either[A, B]) extends AnyVal { | def foreach[U](f: B => U): Unit = e.right.foreach(f) | def map[C](f: B => C): Either[A, C] = e.right.map(f) | def flatMap[C](f: B => Either[A, C]) = e.right.flatMap(f) | } defined class RightBiasedEither scala> scala> def f(c:Char):String = "ha" f: (c: Char)String scala> def either:Either[Int,Char] = Right('r') either: Either[Int,Char] scala> for { | c <- either | s = f(c) | } yield s res0: Either[Int,String] = Right(ha)
        Hide
        Lee Mighdoll added a comment -

        Thanks Jason. That's very helpful. I understand the flaw in Either now. Your solution is nice both to explain and to try out.

        That the stock unbiased Either requires a bit more syntax to use the RightProjection/LeftProjection is apparent from the api, but because of this design flaw it's hard to recommend Either.

        Is there an open issue on fixing Either? Or a consensus that it's not fixable? Seems like the standard lib should get some combination of a fix, interim documentation, or a replacement.

        Show
        Lee Mighdoll added a comment - Thanks Jason. That's very helpful. I understand the flaw in Either now. Your solution is nice both to explain and to try out. That the stock unbiased Either requires a bit more syntax to use the RightProjection/LeftProjection is apparent from the api, but because of this design flaw it's hard to recommend Either. Is there an open issue on fixing Either? Or a consensus that it's not fixable? Seems like the standard lib should get some combination of a fix, interim documentation, or a replacement.
        Hide
        Jason Zaugg added a comment -
        Show
        Jason Zaugg added a comment - I've started a discussion here: https://groups.google.com/d/topic/scala-debate/K6wv6KphJK0/discussion
        Hide
        Rob Dickens added a comment -

        Lee, Jason,

        Have just come up with a version of Either which fixes this. Please would you take a look and let me know what you think:

        http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html

        Cheers, Rob

        Show
        Rob Dickens added a comment - Lee, Jason, Have just come up with a version of Either which fixes this. Please would you take a look and let me know what you think: http://robsscala.blogspot.co.uk/2012/05/fixing-scalaeither-leftrightmap-returns.html Cheers, Rob

          People

          • Assignee:
            Unassigned
            Reporter:
            Lee Mighdoll
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development