2

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)?

sam boosalis
  • 1,997
  • 4
  • 20
  • 32
  • 1
    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 y` – Rado Buransky Apr 22 '14 at 02:11
  • i'd rather not use a backtick variable, and more importantly, it won't work with any type either. – sam boosalis Apr 22 '14 at 18:32

3 Answers3

6

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!

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    I think you're correct about variance (thx), but still `val x: ?[X] = null.asInstanceOf[?[Y]]` is OK for invariant version, `Y extends X`. Am I missing something or is that the subject of those HK variance issues I don't understand? `Pair` et al are annotated. – som-snytt Apr 22 '14 at 05:42
  • @som-snytt Sorry, I don't understand your question. – Daniel C. Sobral Apr 22 '14 at 15:24
  • i mean, if you renamed some java methods in the import or used scala only, it would save typing. but that's not really the point. the point is that it looks "Optiony" which makes it feel more optional when you read it. – sam boosalis Apr 22 '14 at 18:28
  • (i was using scala 2.10.3 and it did compile after all. my bad.) – sam boosalis Apr 22 '14 at 18:29
  • if scala doesn't treat `?` specially, why does the type alias output "$qmark" and why won't `val x? = Some(1)` compile? (i'm using the default namespace i.e. `?` returns `not found`). – sam boosalis Apr 22 '14 at 18:36
  • in fact it does, i've found: http://stackoverflow.com/questions/7656937/valid-identifier-characters-in-scala/7657692 one answer says it's illegal because "illegal because you mix an operator character and a non-operator character". without an underscore. – sam boosalis Apr 22 '14 at 18:41
  • i assume this is to let operator methods be right behind variables, and avoid `val x? = 1` change the lexing of `x?` with some `def ?() = ...`. personally, i'd be ok with mandatory whitespace (or a dot) after operators. – sam boosalis Apr 22 '14 at 18:44
  • @DanielC.Sobral Added the code sample to my answer. Someday soon I'll study it, but maybe you can tell me off the cuff why it compiles. The type alias appears to inherit (so to speak) the variance of the RHS. – som-snytt Apr 22 '14 at 19:06
  • @samboosalis The `$qmark` is a generic name translation; similarly, colon `:` becomes `$colon`; all such operators handled that way. For a given identifier, you need an underscore between alphas and opchars, like `abc_?`. There's a recent question about that. But you want a separate `?` identifier. `val x?` makes no sense, like – som-snytt Apr 22 '14 at 19:15
  • ...like `val x Int`, which lacks a colon. – som-snytt Apr 22 '14 at 19:15
4

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.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • could you please explain a bit about type ?[A,_] = Option[A]? what is _ for? how does this make x: Int ? _ to an Option[Int]? thanks. – user1484819 Apr 22 '14 at 03:14
  • Placeholder `_` for type params is described in 4.4 http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html where here it allows infix notation (in lieu of postfix). Actually, I just noticed that the space isn't needed, see edit. – som-snytt Apr 22 '14 at 03:25
  • Ah ah, I'm still pondering if I would actually use it, but I like the wildcard hack :) – Régis Jean-Gilles Apr 22 '14 at 09:25
  • By the way, you can even remove the leading space, as in `Int?_` – Régis Jean-Gilles Apr 22 '14 at 09:38
  • @RégisJean-Gilles Right, I misread the OP, I thought the space was desirable. I tend to read the OP code and skip the OP text, ;tldr etc. – som-snytt Apr 22 '14 at 09:46
  • Yeah, I'm not sure about the variance thing. I thought it was required, but a type alias is really an alias, so it may make sense that variance does not apply to it. OTOH, if I declare, say, `type JList[+A] = java.util.List[A]`, I get a variance error, so it clearly does pay attention to variance annotations on types. – Daniel C. Sobral Apr 22 '14 at 21:07
4

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))
                  ^
Randall Schulz
  • 26,420
  • 4
  • 61
  • 81