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] {
}
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.
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.
Related
I'm reading ScalaZ tutorial and now I'm on the section of Yes-No type class. Eventual goal is to get 1.truthy to return true. Here is the implementation of the typeclass:
trait CanTruthy[A] { self =>
/** #return true, if `a` is truthy. */
def truthys(a: A): Boolean
}
object CanTruthy {
def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev
def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] {
def truthys(a: A): Boolean = f(a)
}
}
trait CanTruthyOps[A] {
def self: A
implicit def F: CanTruthy[A]
final def truthy: Boolean = F.truthys(self)
}
object ToCanIsTruthyOps {
implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =
new CanTruthyOps[A] {
def self = v
implicit def F: CanTruthy[A] = ev
}
}
implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys({
case 0 => false
case _ => true
})
Looks a little scary to me. We introduced 2 new traits to achieve that. But we can achieve the same just by using implicit classes:
trait CanTruthy {
def truthy: Boolean
}
object CanTruthy{
implicit class CanTruthyInt(i: Int) extends CanTruthy{
override def truthy: Boolean = i match {
case 0 => false
case _ => true
}
}
}
Looks the same to me. So why do we need to use the way in the tutorial? What kind of things I missed? Can you explain what is the difference?
I think the problem here is a misreading of the scope of this sentence:
Eventual goal is to get 1.truthy to return true.
This is what we're trying to do with the CanTruthyOps stuff, but it's not the goal of the CanTruthy type class, and more generally syntactic concerns like this aren't the goal of type classes.
The goal of type classes is to allow us to constrain types in a simple, flexible, compositional way. The type parameter-less CanTruthy approach doesn't really support the simple part or the flexible part or the compositional part very nicely (arguably the implementation of type classes in Scala isn't very simple either, but it's at least a little simpler and definitely more flexible and compositional).
Take this method from the tutorial, for example (lightly modified to avoid the Any):
// Type class style
def truthyIf[A: CanTruthy, B](cond: A)(ifyes: => B)(ifno: => B): B =
if (cond.truthy) ifyes else ifno
If you wanted to translate this into your type parameter-less style, at first things seem pretty good:
// Parameterless style
def truthyIf[B](cond: CanTruthy)(ifyes: => B)(ifno: => B): B =
if (cond.truthy) ifyes else ifno
But now suppose that you needed to keep the original type around. There are lots of reasons this might be necessary—you might want to sort a collection of values with scala.Ordering before you check the truthiness of one of them, for example, or you might have a variation of this method where the original type is also the return type (in the type class style here):
// Type class style
def truthyOrElse[A: CanTruthy](cond: A)(ifno: => A): A =
if (cond.truthy) cond else ifno
Now the translation is less fun:
// Type parameter-less style
def truthyOrElse[A <% CanTruthy](cond: A)(ifno: => A): A =
if (cond.truthy) ifyes else ifno
Where the funky <% thing is syntactic sugar for an implicit parameter:
// Type parameter-less style (desugared)
def truthyOrElse[A](cond: A)(ifno: => A)(implicit evidence$1: A => CanTruthy): A =
if (cond.truthy) cond else ifno
But the : in the type class style is also syntactic sugar:
// Type class style, desugared
def truthyOrElse[A](cond: A)(ifno: => A)(implicit evidence$2: CanTruthy[A]): A =
if (cond.truthy) cond else ifno
Note that these methods look almost identical—in both you're writing a method that requires some implicit evidence (at compilation time) that A is truthy. In the type parameter-less style this evidence is an implicit conversion, while in the type class style it's an implicit value of a generic type.
There are several advantages to the latter approach. One kind of abstract one is that it allows us to separate the "here's some evidence that I know how to do X for this type" concern from the purely syntactic "I can call .x on this thing" concern. Sure, this separation requires some extra machinery (two traits instead of one), but keeping a clean line between the syntactic and semantic issues is arguably worth it.
Another (related) advantage is that the type class can be more efficient, since it allows us to forgo the syntax, and therefore also the extra allocation it involves:
// Type class style, no syntax
def truthyOrElse[A](cond: A)(ifno: => A)(implicit ev: CanTruthy[A]): A =
if (ev.truthys(cond)) cond else ifno
Another advantage comes up in cases where the operation you're trying to provide evidence about involves more than one value:
trait Addable[A] {
def plus(a: A, b: A): A
}
object Addable {
implicit val intAddable: Addable[Int] = new Addable[Int] {
def plus(a: Int, b: Int): Int = a + b
}
}
There's just no nice way to do this kind of thing as an Int => Addable implicit conversion.
The type class approach similarly handles cases where you have multiple types that you need your operation to work on, etc., while the type parameter-less approach doesn't really (at least not in any reasonably clean way).
So to sum up: if you just want some nice enrichment methods that you're generally using in situations where you have concrete types, the type parameter-less approach is totally reasonable and may involve a little less code. If you want to be able to abstract over types that support some operation in an efficient, flexible, generic, and reasonably elegant way, write a type class.
(this is based on the article at http://bertails.org/2015/02/15/abstract-algebraic-data-type)
First, I am defining an abstract version of scala.Option.
import scala.language.higherKinds
trait OptionSig {
type Option[+_]
type Some[+A] <: Option[A]
type None <: Option[Nothing]
}
abstract class OptionOps[Sig <: OptionSig] extends Extractors[Sig] {
def some[A](x: A): Sig#Some[A]
def none: Sig#None
def fold[A, B](opt: Sig#Option[A])(ifNone: => B, ifSome: A => B): B
}
I want to be able to use pattern matching on Sig#Option[A] so Extractors looks like that:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](opt: Sig#Option[A]): scala.Option[A] =
fold(opt)(scala.None, a => scala.Some(a))
}
object None {
def unapply[A](opt: Sig#Option[A]): Option[Unit] =
fold(opt)(scala.Some(()), _ => scala.None)
}
}
Now I can write this program:
class Program[Sig <: OptionSig](implicit ops: OptionOps[Sig]) extends App {
import ops._
val opt: Sig#Option[Int] = some(42)
opt match {
case None(_) => sys.error("")
case Some(42) => println("yay")
case Some(_) => sys.error("")
}
}
And I can test it with this implementation.
trait ScalaOption extends OptionSig {
type Option[+A] = scala.Option[A]
type Some[+A] = scala.Some[A]
type None = scala.None.type
}
object ScalaOption {
implicit object ops extends OptionOps[ScalaOption] {
def some[A](x: A): ScalaOption#Some[A] = scala.Some(x)
val none: ScalaOption#None = scala.None
def fold[A, B](opt: ScalaOption#Option[A])(ifNone: => B, ifSome: A => B): B =
opt match {
case scala.None => ifNone
case scala.Some(x) => ifSome(x)
}
}
}
object Main extends Program[ScalaOption]
It looks like it works but there is one annoying thing I cannot figure out.
With, scala.Option, the type of s in Option(42) match { case s # Some(42) => s } is Some[Int]. But with my snippet above, it is Sig#Option[Int] and I would like to make it Sig#Some[Int] instead.
So I tried the following to be closer to what scalac generates for its case classes:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](s: Sig#Some[A]): scala.Option[A] =
fold(s)(scala.None, a => scala.Some(a))
}
object None {
def unapply(n: Sig#None): Option[Unit] =
fold(n)(scala.Some(()), (_: Any) => scala.None)
}
}
But now I get warnings like the following:
[warn] Main.scala:78: abstract type pattern Sig#None is unchecked since it is eliminated by erasure
[warn] case None(_) => sys.error("")
I am not sure why this is happening as Sig#None is a subtype of Sig#Option[Int] and this is known at compile time.
Also the runtime is still ok, but the inferred type is still not the one I was expecting.
So the questions are
why is type erasure mentioned here despite the subtyping information?
how to get Sig#Option[Int] for s in (some(42): Sig#Option[Int]) match { case s # Some(42) => s }
Unfortunately, you cannot do what you want. The problem is that it is not enough that scalac know that Sig#None <: Sig#Option[A], it must be able to verify that the value it is passing to unapply is, in fact, Sig#None. This works for scala.Option, because the compiler can generate an instanceof check to verify that a type is actually a Some or a None before passing it to the unapply method. If it fails the check, it skips that pattern (and unapply is never called).
In your case, since scalac only knows that opt is a Sig#Option[Int], it cannot do anything to guarantee that the value is actually a Some or None before passing it along to unapply.
So what does it do? It passes the value along anyways! What does this mean? Well, let's modify your extractors a bit:
trait Extractors[Sig <: OptionSig] { self: OptionOps[Sig] =>
object Some {
def unapply[A](s: Sig#Some[A]): scala.Option[A] =
fold(s)(scala.None, a => scala.Some(a))
}
object None {
def unapply(n: Sig#None): Option[Unit] =
scala.Some(())
}
}
All we've done is stopped using fold in the None case. Since we know the argument must be a Sig#None, why even bother calling fold, right? I mean, we wouldn't expect a Sig#Some to be passed here, right?
When we run this example, you'll hit a RuntimeException, because our very first pattern match succeeds and calls ???. In the case of scala.Option, the pattern would fail because it's guarded by a generated instanceof check.
I've gisted another example that shows another danger, where we add a small constraint to Sig#Some, which let's us avoid the fold in the Some case too: https://gist.github.com/tixxit/ab99b741d3f5d2668b91
Anyways, your specific case is technically safe. We know that you used fold and so it is safe to use with an Sig#Option. The problem is that scalac doesn't know that.
Summary of the Haskell these package:
The 'These' type represents values with two non-exclusive possibilities
data These a b = This a | That b | These a b
Is there anything similar in Scala? Maybe in scalaz?
For those unfamiliar with Haskell, here's a rough sketch of how one might approach this in Scala:
sealed trait These[+A, +B] {
def thisOption: Option[A]
def thatOption: Option[B]
}
trait ThisLike[+A] {
def `this`: A
def thisOption = Some(a)
}
trait ThatLike[+B] {
def `that`: B
def thatOption = Some(b)
}
case class This[+A](`this`: A) extends These[A, Nothing] with ThisLike[A] {
def thatOption = None
}
case class That[+B](`that`: B) extends These[Nothing, B] with ThatLike[B] {
def thisOption = None
}
case class Both[+A, +B](`this`: A, `that`: B) extends These[A, B]
with ThisLike[A] with ThatLike[B]
Or you could do something like combining Eithers:
type These[A, B] = Either[Either[A, B], (A, B)]
(Obviously, expressing the data structure is not difficult. But if there's something already well thought-out in an existing in a library, I'd prefer to just use it.)
scalaz has these, which is also referred to as \&/
https://github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/scala/scalaz/These.scala
I know there are already some ways of making traversal generic in scala, but I'm trying to define a simple trait that encapsulates a traversable type. However, I'm having trouble getting the type signatures to work out. A first attempt would be something like the following:
trait Traversable1[B] {
def head: Option[B]
def next: Traversable1[B]
}
This works well, except I want next to return an object that has the same type as the original traversable, not the more generic traversable type. For example, if List has the trait Traversable1, it's next method should return something of type List, not Traversable1. Thus, my second attempt was the following:
trait Traversable2[A[B] <: Traversable2[A[B]] {
def head: Option[B]
def next: A[B]
}
Here, A could equal List, so next would return a List[B]. Unfortunately, this code does not compile: A[B] takes no type parameters, expected: one
How can I achieve what I'm trying to do?
You could create a trait like:
trait Traversable[T[_]] {
def head[A](t: T[A]): Option[A]
def next[A](t: T[A]): T[A]
}
then implement it for lists like:
implicit val listTraversable = new Traversable[List] {
def head[A](l: List[A]) = l match {
case Nil => None
case x::_ => Some(x)
}
def next[A](l: List[A]) = l.tail
}
You can then make use of it using the 'type class' pattern by taking an implicit implementation of the trait to do the work e.g.
def foreach[T[_], A](t: T[A], f: A => Unit)(implicit trav: Traversable[T]): Unit = {
trav.head(t) match {
case Some(v) =>
f(v)
foreach(trav.next(t), f)
case None => ()
}
}
and call it with
foreach(List(1,2,3), println)
From Functional Programming in Scala, I'm trying to implement Either.map.
trait Either[+E, +A] {
def map[B](f: A => B): Either[E, B] = this match {
case Either(x: E, y: A) => Right(f(y))
case _ => Left()
}
}
One error shows up on compilation, among others. I'm not showing them since I appear to be missing the concept of implementing Either.
Either.scala:3: error: value Either is not a case class constructor,
nor does it have an unapply/unapplySeq method
case Either(x: E, y: A) => Right(f(y))
Please advise me on implementing it.
The error message says that you cannot use Either as a case class constructor. IOW, Either is equivalent to an abstract class since you have encoded it as a trait with its own implementable methods. Let's assume you have the following encoded representation of Either, Left and Right:
sealed trait Either[+E, +A] {
def map[B](f: A => B): Either[E, B] = ???
}
// Left signifies error condition
// Right is something Right, along with its message.
case class Left[+E](err: E) extends Either[E,Nothing]
case class Right[+E](msg: E) extends Either[Nothing,E]
You can write the map function as:
def map[B](f: A => B): Either[E, B] = this match {
case Right(v) => Right(f(v))
case Left(e) => Left(e)
}
Here I'm simply saying that if you encounter something that's supposed to be a right value, perform some function computation on it and return it exactly as it should be - a Right. Since Either is a sealed trait(mainly for convenience), the only other type could be a Left which I return as is. Read more about Either
try this one
trait Either[+E, +A] {
def map[B](f: A => B): Either[E, B] = this match {
case Right(y) => Right(f(y))
case left: Left[E] => left
}
}