Multiple type parameter lists in Dotty? (SI-4719)


#1

I recently ran into an situation where I wanted to provide some type arguments to a method manually, but have the others be inferred.

It was something like this

def retry[E <: Throwable: ClassTag,T](block: => T): T = {
...
}

Where basically I want to retry a certain block of code that will produce a type T in the event of an exception of type E. I want the type T to be inferred but I want to explicitly specify type E. I was able to solve this by wrapping the whole thing in a class which takes the type E as a parameter and defining retry as a method on that class,

    def forThrowable[E <: Throwable : ClassTag]: ForThrowable[E] = new ForThrowable[E]
    class ForThrowable[E <: Throwable : ClassTag] {
      def retry[T](block: => T): T = ...
    }

but it feels a bit unnecessary.

It seems being able to have multiple type parameter lists would solve this issue.

def retry[E <: Throwable: ClassTag][T](block: => T): T = {
...
}

There’s an SI for this issue as well https://issues.scala-lang.org/browse/SI-4719.

Are there currently any plans for this in Dotty? Thanks


#2

Dotty has (or will have) named type parameters which solve this particular usecase. Nontheless, multiple type parameters lists would probably make language more regular, so maybe worth investigating.


#3

Also worth noting that Dotty already supports curried type lambdas, as in:

scala> type M = [A] => [B] => Map[A,B]
// defined alias type M = [A] => [B] => Map[A, B]
scala> Option.empty[M[Int][String]]
val res0: Option[M[Int][String]] = None

So it would make sense to allow multiple parameter lists in types with the same meaning:

type M[A][B] = Map[A,B]

…and to generalize that syntax to methods.


#4

Named type parameters can be used for this, but they’re not the ideal solution imho. They require the caller of the method to know the names of the type parameters and to know which type parameters are supposed to be inferred and which are not. Plus it’s pretty verbose at the call site.


#5

You can always define a type with names that you like, i.e. write something like

type M[KeyILike, ValueILike] = Map[KeyILike, ValueILike]

Thus, you can define names you like if it is appropriate for you to define your own type-aliases.

Do you think it is a deal of the declaration site to define the order of the several type parameter lists? I.e., for example, imagine that Map is defined with (potential) multi-type-parameter lists as

trait Map[K][V] ...

and we can use Map[Int] to forge V be inferred.

Those who need V be inferred are happy, but those two need K be inferred would need to define something like

type M[V][K] = Map[K, V]

to have the K type parameter at the end.
I think this type of solution is not better than defining type-aliases with known name (like above) or ad-hoc type lambdas.


Personally, I think that type lambdas-based stuff should somehow solve it, like maybe using

[T] => retry { ... }

at the call-site of the topicstarter’s example. I think so because it seems that user-site knows which type parameters should be inferred and which not rather than the definition-site.


#6

This is about methods where I think type aliases have little relevance. Indeed class types like Map may be a better fit for named type parameters. In my experience for methods it are almost always the same parameters that should be explicitly provided. E.g. in the example given in the OP it doesn’t make any sense to explicitly provide T and let E be inferred (it would be Nothing…).


#7

I agree @Jasper-M. Also it seems from SI-4719 that another benefit of multiple type parameter lists would be to help with implicit resolution, especially for code doing a lot of type-level computations. @LPTK, would using using the approach you described confer this benefit as well (being able to solve implicits per type param list), or would it just work for the example I posted?


#8

To be clear, I didn’t describe any approach. I just meant to show that it would make sense to have multiple type parameter lists for both types and methods, from the point of view of language consistency.


#9

Oh I see