Inference of a function result type

75 views
Skip to first unread message

Anatoliy Kmetyuk

unread,
Jul 14, 2015, 1:44:49 PM7/14/15
to scala-i...@googlegroups.com
I have the following code:
  import scala.reflect.runtime.universe.{TypeTag, typeOf}  

  trait Root
  class A extends Root
  class B extends Root

  class Monad[T <: Root](tag: TypeTag[T]) {
    def f: T = {
           if (tag.tpe =:= typeOf[A]) new A
      else if (tag.tpe =:= typeOf[B]) new B
      else null
    }.asInstanceOf[T]
  }

  def createMonad[T <: Root](implicit tag: TypeTag[T]): Monad[T] = new Monad[T](tag)
  def createRoot [T <: Root](implicit tag: TypeTag[T]): T = createMonad[T].f

  def fOrd[T        ](x:       T ) = x
  def fMon[T <: Root](x: Monad[T]) = x

Now, when I do:
scala> fMon[A](createMonad)
res6: playground.Playground.Monad[playground.Playground.A] = playground.Playground$Monad@42de1824

The result is Monad[A] - createMonad is called with a correctly inferred A as a type parameter.
However, when I do:
scala> fOrd[A](createRoot)
res7: playground.Playground.A = null

scala> fOrd[A](createRoot[A])
res8: playground.Playground.A = playground.Playground$A@7d7c1edb

In first case, the type parameter for createRoot fails to be inferred correctly, createRoot is called with Nothing as a type parameter.

So it seems that in the argument position, only the type parameters of the type of the function's result are inferred. Scala doesn't infer the type of the function's result itself.

Why so? And how can I still call createRoot without the type parameter and have it inferred correctly?

Jon Pretty

unread,
Jul 14, 2015, 3:10:52 PM7/14/15
to scala-internals
Hi Anatoliy,

In the case of

   fOrd[A](createRoot)

You have specified `A` explicitly, so that is now fixed, and the expected return type of the parameter is therefore `A`. So far, so good.

What that means is that you can provide any parameter whose type is a subtype of `A`. As far as the compiler is concerned, `Nothing` is as good a type as any, so it chooses `Nothing` in the absence of any other constraints on the type. In theory, it could equally choose `A` or an arbitrary subtype of `A`, but type inference is solving a system of constraints, and the parameter `T` in `createRoot` is underconstrained.

So, why does this case do what we want?

   fMon[A](createMonad)

The analysis is the same up to the application of `createMonad`. The expected type of the parameter is `Monad[A]`, which means that you can provide any parameter whose type is a subtype of `Monad[A]`. But because `T` is *invariant* in `Monad[T]`, that `T` must be exactly the same `A` for *all* subtypes of `Monad[A]`.

Put another way, `Monad[T]` can vary without `T` varying, but `T` cannot vary without `T` varying.

Hope that helps.

Cheers,
Jon

--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Jon Pretty | @propensive

Anatoliy Kmetyuk

unread,
Jul 15, 2015, 10:49:27 AM7/15/15
to scala-i...@googlegroups.com
Thank you, Jon. I get it now. But how can I restrict the lower type bounds for T to be not Null or Nothing? T >: Null restricts Nothing, but not Null.

Anatoliy Kmetyuk

unread,
Jul 15, 2015, 11:51:08 AM7/15/15
to scala-i...@googlegroups.com
Or, even better, how do I make a function "invariant" on its type arguments - meaning if it is T, then infer T and don't allow any subclasses?

Jon Pretty

unread,
Jul 21, 2015, 5:29:57 PM7/21/15
to scala-internals
Hi Anatoliy,

Sorry it's taken so long to reply to this. I had a couple of attempts to modify the code to do what you want, but I didn't manage it. In all likelihood, if I encountered the same problem, I would probably do some wider refactoring to avoid that exact requirement occurring.

Sorry I can't be more useful here.

Cheers,
Jon

On 15 July 2015 at 17:51, Anatoliy Kmetyuk <anatoli...@gmail.com> wrote:
Or, even better, how do I make a function "invariant" on its type arguments - meaning if it is T, then infer T and don't allow any subclasses?
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Jon Pretty | @propensive
Reply all
Reply to author
Forward
0 new messages