What's the deal with all the Either cruft?

前端 未结 4 1799
Happy的楠姐
Happy的楠姐 2020-12-23 14:04

The Either class seems useful and the ways of using it are pretty obvious. But then I look at the API documentation and I\'m baffled:

def joinLeft [A1 >:          


        
4条回答
  •  一生所求
    2020-12-23 14:24

    My suggestion is add the following to your utility package:

    implicit class EitherRichClass[A, B](thisEither: Either[A, B])
    {
       def map[C](f: B => C): Either[A, C] = thisEither match
       {
         case Left(l) => Left[A, C](l)
         case Right(r) => Right[A, C](f(r))
       }
       def flatMap[C](f: B => Either[A, C]): Either[A, C] = thisEither match
       {
         case Left(l) => Left[A, C](l)
         case Right(r) => (f(r))
       }
    }   
    

    In my experience the only useful provided method is fold. You don't really use isLeft or isRight in functional code. joinLeft and joinRight might be useful as flatten functions as explained by Dider Dupont but, I haven't had occasion to use them that way. The above is using Either as right biased, which I suspect is how most people use them. Its like an Option with an error value instead of None.

    Here's some of my own code. Apologies its not polished code but its an example of using Either in a for comprehension. Adding the map and flatMap methods to Either allows us to use the special syntax in for comprehensions. Its parsing HTTP headers, either returning an Http and Html error page response or a parsed custom HTTP Request object. Without the use of the for comprehension the code would be very difficult to comprehend.

    object getReq
    {      
      def LeftError[B](str: String) = Left[HResponse, B](HttpError(str))
      def apply(line1: String, in: java.io.BufferedReader): Either[HResponse, HttpReq] = 
      {
        def loop(acc: Seq[(String, String)]): Either[HResponse, Seq[(String, String)]] =
        {
          val ln = in.readLine
          if (ln == "")
            Right(acc)         
          else
            ln.splitOut(':', s => LeftError("400 Bad Syntax in Header Field"), (a, b) => loop(acc :+ Tuple2(a.toLowerCase, b)))
        }
    
        val words: Seq[String] = line1.lowerWords
    
        for
        {
          a3 <- words match
          {
            case Seq("get", b, c) => Right[HResponse, (ReqType.Value, String, String)]((ReqType.HGet, b, c))
            case Seq("post", b, c) => Right[HResponse, (ReqType.Value, String, String)]((ReqType.HPost, b, c))
            case Seq(methodName, b, c) => LeftError("405" -- methodName -- "method not Allowed")
            case _ => LeftError("400 Bad Request: Bad Syntax in Status Line")
          }
          val (reqType, target, version) = a3
          fields <- loop(Nil)
          val optLen = fields.find(_._1 == "content-length")
          pair <- optLen match
          {
            case None => Right((0, fields))
            case Some(("content-length", second)) => second.filterNot(_.isWhitespace) match
            {
              case s if s.forall(_.isDigit) => Right((s.toInt, fields.filterNot(_._1 == "content-length")))
              case s => LeftError("400 Bad Request: Bad Content-Length SyntaxLine")
            }
          }
          val (bodyLen, otherHeaderPairs) = pair
          val otherHeaderFields = otherHeaderPairs.map(pair => HeaderField(pair._1, pair._2))
          val body = if (bodyLen > 0) (for (i <- 1 to bodyLen) yield in.read.toChar).mkString else ""         
        }      
        yield (HttpReq(reqType, target, version, otherHeaderFields, bodyLen, body))
      }   
    }
    

提交回复
热议问题