2
votes

i tried

type ?[_] = Option[_]

def f(x: ?[Int]) = for (y <- x) yield y

(but i don't know what i am doing.)

insofar as types are just objects, i should be able to define a postix operator (i.e. zero arity method) for use in type signatures (i think). it might need a space like

def f(x: Int ?) = for (y <- x) yield y

scala makes it easy to use the Option type with matching and polymorphism, avoid null. but, most classes are (nullable) vars and java often returns vars. using classes and calling java are two of scala's selling points. an easy-to-write and easy-to-read syntax would support Options even more strongly.

  1. what are all the things that scala does with "?" that make its parsing special.

  2. ideally, one could write

    def f(x: Int?) = for (y <- x) yield y

like other languages. can we do this in scala (without a macro)?

3
I know this is not what ypu want, but you can do something like this type `Int?` = Option[Int] ... and then def f(x: `Int?`) = for (y <- x) yield yRado Buransky
i'd rather not use a backtick variable, and more importantly, it won't work with any type either.sam boosalis

3 Answers

6
votes

First, types are not objects. In fact, Scala has exactly two namespaces: values and types. They are very different things, and play by very different rules.

The postfix idea is kind of nice, actually, but it is not possible. There's an infix notation for types, though.

Now, to what you wrote:

type ?[_] = Option[_]

Each underscore has a different meaning. The underscore in ?[_] means ? is higher-kinded, but you don't care what it's type parameter is. The underscore in Option[_] means Option is an existential type. So when you write x: ?[Int], Scala will convert it to x: Option[t] { forSome type t }. This means that not only you don't get the Int, but the type parameter of Option is unknowable (you just know it exists).

However, it does compile:

scala> def f(x: ?[Int]) = for (y <- x) yield y
f: (x: ?[Int])Option[Any]

Which version of Scala did you use? 2.11? A co-worker of mine has already found some type inference regressions on 2.11, so that could be it.

The proper way to write the type alias would be this:

type ?[+A] = Option[A]

Not only we pass the type parameter along (it is a parameter, after all!), but we need to specify co-variance for it to act just Option (which is co-variant itself).

Now, as to your two questions, Scala has absolutely no special treatment of ?. And, no, you can't do this. This ? is not exactly widespread among languages either, and in all of them that support it, it is built in the language, and not something externally defined.

Besides, it's kind of a joke that, when interface with Java, typing out Option would be a problem -- not with the average identifier size in Java!

4
votes

You intended to get an Option[Int] out:

scala> type ?[A] = Option[A]
defined type alias $qmark

scala> def f(x: ?[Int]) = for (y <- x) yield y + 1
f: (x: ?[Int])Option[Int]

and it does compile anyway.

You could maybe

scala> type ?[A,_] = Option[A]
defined type alias $qmark

scala> def f(x: Int ? _) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]

or similar.

scala> def f(x: Int ?_) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]

looks more postfixy.

P.S. Still curious whether variance annotation on type alias is required or merely advisable.

scala> type ?[A] = Option[A]
defined type alias $qmark

scala> trait X ; trait Y extends X ; trait Z extends X
defined trait X
defined trait Y
defined trait Z

scala> val x: ?[X] = null.asInstanceOf[?[Y]]  // why is this OK?
x: ?[X] = null

scala> class C[A]
defined class C

scala> val c: C[X] = null.asInstanceOf[C[Y]]  // like this is not OK
<console>:10: error: type mismatch;
 found   : C[Y]
 required: C[X]
Note: Y <: X, but class C is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
       val c: C[X] = null.asInstanceOf[C[Y]]
                                      ^

Maybe compare SI-8522 and related issues.

4
votes

You might consider a renaming import. When you create a type alias you only rename a type. When you rename a symbol during import you include all referents of that name, both type and value.

To wit:

scala> import scala.{Option => ?}
import scala.{Option=>$qmark}

scala> val oi1: ?[Int] = Some(1)
oi1: Option[Int] = Some(1)

scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: Option[Int])Int

scala> mi1(None)
res1: Int = -1

scala> mi1(?(1))
res2: Int = 1

Compare with this:

scala> type ?[A] = Option[A]

scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: ?[Int])Int

scala> mi1(?(1))
<console>:10: error: not found: value ?
              mi1(?(1))
                  ^