Scala's choice in implementing Option

2020-03-04 08:09发布

问题:

How it's done in Scala:

  sealed trait Option[+A] {
    def get: A
    def isEmpty: Boolean
    def map[B](f: A => B): Option[B] =
      if (isEmpty) None else Some(f(this.get))
  }
  object None extends Option[Nothing] {
    def isEmpty = true
    def get = throw new NoSuchElementException("None.get")
  }
  case class Some[+A](x: A) extends Option[A] {
    def isEmpty = false
    def get = x
  }

How I would assume it in OOP world:

  sealed trait Option[+A] {
    def map[B](f: A => B): Option[B]
  }
  object None extends Option[Nothing] {
    def map[B](f: Nothing => B): Option[B] = this
  }
  case class Some[+A](get: A) extends Option[A] {
    def map[B](f: A => B): Option[B] = Some(f(get))
  }

What's wrong with the latter?

Functional programming in Scala is using match in Option[A] trait, which is the third way (looks like Haskell, but why?) Why not utilize subtype polymorphism?

UPDATE: Third way I mentioned:

sealed trait Option[+A] {
  def map[B](f: A => B): Option[B] = this match {
    case None => None
    case Some(a) => Some(f(a))
  }
}
object None extends Option[Nothing] {
}
case class Some[+A](get: A) extends Option[A] {
}

回答1:

I'm not sure whether you intended to, but you left out the declarations of isEmpty and get, which are needed by anyone that wants to check the contents of an arbitrary Option without needing to downcast to Some. Since both of these methods need to be defined by both subclasses, and since map can be defined in terms of them, I think the reasoning was that it would be better to define map in one place leveraging the subclass implementations of the other methods, rather than defining map in three places.



回答2:

I guess that since scala support both functional and imperative and it aiming java programs, this is one of the stuff that make them fill more comfortable.
From java programmer

val opt: Option[String] = ???
if (!opt.isEmpty) {
  //do something with opt.get
} else {
  //do some default...
}

may be more understandable than functional way (even with getOrElse).

@Victor Moroz wrote in one of the comments about using pattern matching instead of the this match { None => default_val; Some(v) => v }
This in addition to be harder to read by programmer new to the functional world, will cost much more, since Option is intended to be use a lot, and instanceOf cost more than a simple if.



标签: scala