Reputation: 63062
From the canonical "Programming in Scala" from Oderskey and Venners:
scala> val results = List(Some("apple"), None,
Some("orange"))
results: List[Option[java.lang.String]] = List(Some(apple),
None, Some(orange))
scala> for (Some(fruit) <- results) println(fruit)
apple
orange
I do not understand the philosophy of scala to impose on the programmer the need to explicitly mention Some(apple) as opposed to inferring it. I would prefer to write/see the following:
scala> val results = List("apple", None, "orange")
results: List[Option[java.lang.String]] = List(apple, None, orange)
scala> for (fruit <- results) println(fruit)
apple
orange
Or maybe at the least the following (providing typing at the list level but not at the individual item level):
scala> val results :List[Option[String]] = ("apple", None, "orange")
results: List[Option[java.lang.String]] = List(apple, None, orange)
scala> for (fruit <- results) println(fruit)
apple
orange
At least in this last case: the type of the List is being provided (to help the compiler..) but we still avoid the verbosity of "boxing" every element in the List like Some('foo').
Anyone out there who is better in tune with scala's way of thinking can tell me why I should have to do that extra typing .. ?
Edit: so the following does what I want for Strings.
scala> implicit def toOpt(a : String) = Some(a)
toOpt: (a: String)Some[String]
scala> val myList : List[Option[String]] = List("first", None, "third")
myList: List[Option[String]] = List(Some(first), None, Some(third))
If someone can show how to generalize the above using higher kinded types I will award the answer.
Upvotes: 1
Views: 495
Reputation: 1124
For use in limited sections of code, you can use implicits for that:
implicit def string2SomeString(s: String): Option[String] = Some(s)
val l: List[Option[String]] = List("a", None, "b")
You may experience difficulties when ambiguities occur, however.
A more general implicit is doable but may lead to even more ambiguities:
implicit def a2SomeA[A](s: A): Option[A] = Some(s)
Upvotes: 0
Reputation: 1143
I don't endorse this but what you seem to be looking for is this:
scala> implicit def aToOptionA[T](a:T):Option[T]=Some(a)
warning: there were 1 feature warning(s); re-run with -feature for details
aToOptionA: [T](a: T)Option[T]
scala> val myList : List[Option[String]] = List("first", None, "third")
myList: List[Option[String]] = List(Some(first), None, Some(third))
scala> val myList2:List[Option[Int]] = List(1, None, 2)
myList2: List[Option[Int]] = List(Some(1), None, Some(2))
Upvotes: 3
Reputation: 62835
Don't think I'm rude, but the answer is because compiler isn't some magician that is intended to guess your wishes.
It will help you in case there is boilerplate (type inferring, implicit conversions), but the whole point of compiler (at least for a language with typesystem) is to catch ambiguous situations like
val results :List[Option[String]] = ("apple", None, "orange")
and yell on you Hey dude, you must be doing something wrong!. You're basically asking why the compiler does not allow me to assign dog to the cat, orange to the apple or trick sorter game and when language allows such tricks some very bad and nasty things are happen.
for (fruit <- results) println(fruit)
How should language know that you want to print underlying unboxed value and not the option itself? What should it do if you use some other function? What if I have Option[Option[String]]
? You see, there are many things to consider and if we really deem extra typing as a real problem in this particular case we have to sit down and enumerate all those cases in large book and everyone has to check it out to understand every possible outcome otherwise code is unpredictable. Note also, that there will be hidden performance sinks: e.g. compiler will implicitly convert one collection to another, they're different classes and different structures after all and surprise -- you don't know where. At least without some extra look on your code. After all, why don't you expect
val i: List[Int] = 3
to be valid?
Upvotes: 4
Reputation: 33637
Because it will lead to some serious ambiguity.
Check the below snippet (based on your suggest sample).
List("apple", "hello", "orange")
Is the above code List[Option[String]]
or List[String]
, well both are valid types, which one to choose?
UPDATE:
Lets take another case class example (Option
is a case class with 2 cases Some
and None
):
abstract class Superhero
case class Superman(health : Int) extends Superhero
case class Spiderman(health: Int) extends Superhero
Now we are faced with another ambiguity, if we say:
List[Superhero](100,100)
, Is the first 100 for Superman or is it for Spiderman, same to next 100.
Upvotes: 3
Reputation: 28511
In addition to Ankur's answer, Option
is a Monad in Scala, means it fully implements map
, flatMap
.
This way you can use it in for comprehensions
:
def sum(a: Some[Int], b: Some[Int]): Int = {
for {
a <- maybeA
b <- maybeB
...
} yield {
a + b
}
} getOrElse 0
Or map
over it's contents: val a = Some(5); a map (3 + ) // gives you Some(8)
The type signatures of monadic methods are complicated and difficult for the compiler to deal with.
More ambiguity over identity between the Optional[A]
and A
is useless and simply not possible.
Verbosity
Your claim is not well grounded. In practice you will find scala.Option is extremely powerful.
Say you have:
def methodA: Option[ComplexType]
def methodB: Option[MoreComplexType]
def methodC: Option[InsanelyComplexStructure]
Yet to chain and check for existence, all you need is a comprehension:
for {
a <- methodA
b <- methodB
c <- methodC
} yield {
// now this gets executed only if all the 3 methods return Some()
}
This becomes extremely powerful. Deeply nested if statements are a bad old memory.
Upvotes: 4