Ability to force the caller to specify a type parameter for a polymorphic method


#9

I think there’a a generic way to tell that some generic bound shouldn’t be set to Nothing. I’ve created typeclass NotNothing here:

import scala.annotation.implicitAmbiguous

object NoNothing {
  class NotNothing[T]

  object NotNothing {
    implicit def good[T]: NotNothing[T] = null

    @implicitAmbiguous("Specify generic type other than Nothing")
    implicit def wrong1: NotNothing[Nothing] = null
    implicit def wrong2: NotNothing[Nothing] = null
  }

  def method[T: NotNothing](message: String): Unit = println(message)

  def main(args: Array[String]): Unit = {
    method[Int]("Hello!")
    import NotNothing.wrong1 // comment out to see compilation error
    method[Nothing]("Forced Nothing!")
  }
}

Instead of method[required T] we have method[T: NotNothing]


#10

That is exactly what the SO answer proposes too, and what I claimed to be the solution :wink: Thanks for writing down the code snippet in the thread, I’m sure it will be useful for others in the future.


#11

Note that this trick for implicit negation won’t work in Dotty, instead one must use the Not class from the standard library, see https://github.com/lampepfl/dotty/blob/master/library/src/scala/implicits/Not.scala (we can still get code to cross-compile if we get this class in the Scala 2 standard library).


#12

OK, I’ve missed that SO answer. Anyway, I think my code snippet is more straightforward :slight_smile:

As for Dotty, I haven’t yet started playing with it.


#13

In Dotty, ambiguity is a global error

What does that mean?


#14

See point 4 of http://dotty.epfl.ch/docs/reference/changed/implicit-resolution.html


#15

The following ticket keeps track of the addition of Not to the Scala 2 standard library for 2.13.0-M5:


#16

I have tested this code snippet in scastie . It works correctly.

I don’t understand how Not class can help in this task.

I think It would be better if there was anotation like:

@implicitError("Specify generic type other than Nothing")
implicit def wrong1: NotNothing[Nothing] = null

#17

Sometimes it makes sense to omit a type parameter, because it is not needed, like

**Welcome to Scala 2.12.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
^

def m[T](opt: Option[T]): String = opt.map(x => "I got some " + x.toString).getOrElse(“I got nothing.”)
m: [T](opt: Option[T])String

m(None)
res1: String = I got nothing.**

In this case, we don’t want an error and Nothing makes perfect sense. Should we instead have a type Unknown? Would Unknown ever behave differently from Nothing?


#18

In that case Scala is inferring Nothing because it has information that suggests Nothing, namely that None extends Option[Nothing]. That’s fine by me. What I’m arguing is that Scala should kick the habit of making inferences when it has zero information at all about what to infer. At that point Nothing is not even a reasonable guess, it’s simply picking Nothing because it doesn’t want to give up.


#19

Oh, I’m sorry, that was a bad example. Here is a better one:

**Welcome to Scala 2.12.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.

def isEmpty[T](iterable: Iterable[T]): Boolean = iterable.isEmpty
isEmpty: [T](iterable: Iterable[T])Boolean

isEmpty(List.empty)
res0: Boolean = true**

List.empty[A] has a type parameter, but it is not needed here.


#20

I see your point, but I think if you put such cases on one side of the scale and cases where Nothing is a bad choice, I’m not sure who wins.


#21

In my experience most of cases where inferring Nothing is bad comes from flawed Option.fold implementation. Consider following code:

def optionToList[T](option: Option[T]): List[T] =
  option.fold(List.empty[T] /* skipping T here will result in compilation error */)(value => List(value))

We can skip parameter for List.empty if we use Option.map + Option.getOrElse:

def optionToList[T](option: Option[T]): List[T] =
  option.map(value => List(value)).getOrElse(List.empty /* or even: Nil */)

Disclaimer: code wasn’t tested.


#22

Perhaps a dumb idea. If there was another type between Any and Nothing which we call Something, where Something is also a subtype of all other types, except Nothing, then one could create a lower-bound with >: Something, where you don’t want Nothing to be inferred.

So for cases where Nothing is inferred but the lower bound is Something the compiler will generate an error.


#23

Yes, but there it isn’t that Nothing is a choice to avoid, it’s that the compiler is to short-sighted. (Dotty fixes that.)


#24

I’m interested, is there any case with implicit arguments where such algorithm has sence?
In my experience such implicit substitution will cause error in runtime.

 def load[T](id:Long)(implicit t:TypeTag[T]) = {
   sql(s"""
     select * from ${getTabel(t)} where id = $id
   """)
}

load(10) // It has no sence,since table is not defined.

#25

A similar idea is if there were a way to express exclusive type bounds, so you could say, supertype of Nothing except Nothing. Exclusive upper bounds would also be nice, at least for excluding Any.

Except that it would only work on the static type, not the actual value of course. So you couldn’t keep out nulls that way.


#26

If the compiler inferred Something, would that be better than Nothing? Or would it be forbidden from inferring Something? Would there be any use for Something other than being not Nothing?

Besides, Something and Nothing are really identical. Two types are identical if they have the same instances. If Something is subtype of all types, it has no instances, the same as Nothing.

Seems better to have an annotation NotNothing

def doSomething[@NotNothing T](thing: T) = …


#27

Hmm, sounds like a compiler plugin project for someone to do…


#28

I mean that Something is not really something (heh) that can be inferred