Asserting compile-time types

Given this function:

def f(): String & Option[String] = null

I’d like to catch, at compile time, that the return type is not String as it should be. I’ve been able to do it like this:

var v = f(); v = v : String // fails at compile time

but I was hoping for something cleaner. I tried to define a function hasType[A] so I could write:

hasType[String](f())

or even:

val v = f()
hasType[String](v)

to trigger a compile-time failure, but without success (after playing around with =:= and <:<).

(If you’re wondering about the context, I use assertCompiles and assertTypeError to check that students have written the desired Scala signatures, but I don’t want them to circumvent an exercise on polymorphism by using silly signatures like String & Option[String].)

1 Like

This one liner kind of works.

def hasType[A] = [B] => (b: B) => (_: B =:= A) ?=> b
scala> hasType[String](f())
-- [E172] Type Error: ----------------------------------------------------------
1 |hasType[String](f())
  |                    ^
  |                    Cannot prove that String & Option[String] =:= String.
1 error found
1 Like

I was close! I got the generic lambda, but I got stuck on how to incorporate the implicit evidence in it. I keep forgetting about these context functions.

I also tried this:

def hasType[U] =
   def e[S](x: S)(using ev: U =:= S) = ev
   e

which cannot be compiled.

Thanks a lot!

(P.S. What is the return type of your hasType, if I wanted to write it explicitly?)

Right, in scala 2 you would have to do something like this instead:

class PartiallyApplied[A] {
  def apply[B](b: B)(implicit _: B =:= A) = b
}
def hasType[A] = new PartiallyApplied[A]

I think that theoretically your attempts could work as well, but eta-expansion to polymorphic functions doesn’t work yet IIRC.

And I thought there were some efforts ongoing to add multiple type parameter lists to defs in scala 3 which could simplify this a lot to this:

def hasType[A][B](b: B)(using B =:= A) = b

Although I don’t remember if [A][B] will be allowed or if they have to be separated by value parameter lists.

1 Like

Turns out that this already exists as an experimental feature, but like I suspected it doesn’t allow “unseparated” parameter lists for some reason. So you have to uglify it a little bit:

import scala.language.experimental.clauseInterleaving
def hasType[A](using DummyImplicit)[B](b: B)(using B =:= A) = b
1 Like