1

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.

WestCoastProjects
  • 58,982
  • 91
  • 316
  • 560
  • You could write `for (fruit <- results.flatten) println(fruit)` if you don't like the pattern deconstruction. – Ben James Oct 15 '13 at 11:21
  • My 'beef' is not with the pattern deconstrution but rather with the Some("apple"), Some("orange"), etc. – WestCoastProjects Oct 15 '13 at 11:28
  • Is `val results :List[Option[String]] = ...` really that much less verbose? I suspect if the language was designed along those lines you would have to give the compiler many more type hints than you do now. – Shadowlands Oct 15 '13 at 12:01
  • 2
    I don't really understand your hypothetical example. "apple" is a String, not an Option[String]. Maybe you are thinking of None as something analoguous to null? They are very different conceptually; see http://stackoverflow.com/q/2079170/90874. – thSoft Oct 15 '13 at 13:23
  • 1
    A box with a kitten in it isn't a kitten. It's a box. – Seth Tisue Oct 15 '13 at 21:40

5 Answers5

4

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.

flavian
  • 28,161
  • 11
  • 65
  • 105
  • AFA the type signatures: What about my second presented alternative - in which the type of the map is explicitly declared List[Option[String]] ? The part that bugs me is 'boxing' every element in the list with Some('foo') – WestCoastProjects Oct 15 '13 at 11:33
  • The second alternative (added after your comments and from Ankur) is explicit about the typing: "Option": List[Option[String]]. I'm ok with that. Is there any mechanism to avoid the Some('foo') for EVERY element in the list? – WestCoastProjects Oct 15 '13 at 11:41
  • `val l = None :: List(2, 4).map(Some(_))` if you have bunches of Some? – scand1sk Oct 15 '13 at 15:10
4

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?

Ven
  • 19,015
  • 2
  • 41
  • 61
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
3

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.

Ankur
  • 33,367
  • 2
  • 46
  • 72
  • I have updated the question with an additional option. As it stands the Option/Some seems unneccessarily intrusive/ verbose. – WestCoastProjects Oct 15 '13 at 11:24
  • @javadba: Please see updated answer for your updated question – Ankur Oct 15 '13 at 11:36
  • The second alternative is explicit about the "Option": List[Option[String]] so i'm not clear how the Superman/Spiderman type ambiguity applies here? – WestCoastProjects Oct 15 '13 at 11:39
  • The ambiguity applies to the values you passed to List. The values 100. Both Spiderman and Superman constructor takes a integer. You have passed 100 as value, now how to detect which constructor i.e Spiderman or Superman to use to create the list objects. – Ankur Oct 15 '13 at 11:42
  • The Some/None could detect "None" entries in the list and use the None value for those list entries - and the String constructor for all others. That distinction would not be possible in your Superman class. So this does not seem to be a a perfect analogy. – WestCoastProjects Oct 15 '13 at 11:49
  • 2
    Your question is about type inference which doesn't apply to some specific type in the language BUT to all the types that you can ever define. There is no compiler with type inference which say "hey I work for Some type but I wont work for your own data types" – Ankur Oct 15 '13 at 11:53
  • Hi Ankur, I was just wondering why it were considered "OK" to need to do the Boxing of every element in a List. Scala attempts to be less verbose than java in general - but not in this case. – WestCoastProjects Oct 15 '13 at 11:57
  • It is not boxing. Some is a data type (rather a generic data type). So basically Some("Hello") is a different thing then "Hello". In Java if you have a class User with constructor which takes string, then you won't say `new User("Ankur")` is same as "Ankur" string – Ankur Oct 15 '13 at 12:00
  • I realize that Some(foo) != foo but this is a core construct in Scala and could have been done differently - maybe through implicit conversions for example. – WestCoastProjects Oct 15 '13 at 12:08
  • Well.. Some is part of Scala standard library which is different from Scala core - which is the Scala language specification. – Ankur Oct 15 '13 at 12:11
  • Yes, i was also aware it is Std lib not the language spec - but the "core construct" comment still applies: Some/None is a core idiomatic construct in scala. I am trying to determine when/where to use scala vs python and this is a mild pain point. Creating literal lists is a common exercise when I write test cases. Thanks for the inputs here. – WestCoastProjects Oct 15 '13 at 12:19
2

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))
Mark Lister
  • 1,103
  • 6
  • 16
0

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)
scand1sk
  • 1,114
  • 11
  • 25
  • thanks, I had added the implicits to my question as sort of a partial response. If you can make this more generic using higher-kinded types i will award the answer. – WestCoastProjects Oct 15 '13 at 17:35