3

In scala programming language, given I'll annotate an expression with some broader type and I provide an narrow value, my program is rejected:

scala> def x[A](): A = 8 
<console>:11: error: type mismatch;
found   : Int(8)
required: A
      def x[A]() = 8: A

Whereas in ocaml, when I'll do the same, the program is accepted, but the type annotation of the expression is overriden by the type of narrower expression.

utop # let x (): 'a = 8 ;;
val x : unit -> int = <fun>

What is the difference between these type systems, that results in situation, where in one case the program is rejected whereas in the other it is accepted?

ryskajakub
  • 6,351
  • 8
  • 45
  • 75

3 Answers3

7

@craigfe actually just wrote a very accessible post about this yesterday, and I would highly recommend reading that.

But the short answer is that type variables in annotations in OCaml are unification variables, not polymorphic type constraints. They represent unknown types to be inferred by the compiler, which will prefer more general solutions and might infer it to be polymorphic, but if not possible will infer it to be a specific type.

In order to get the behaviour you expect, you have to explicitly indicate that the type variable should be universally quantified using 'a., usually read "for all a's":

utop # let x: 'a. unit -> 'a = fun () -> 8 ;;
                               ^^^^^^^^^^^
Error: This definition has type unit -> int which is less general than
         'a. unit -> 'a
glennsl
  • 28,186
  • 12
  • 57
  • 75
2

I don't know ocaml at all. But according to: val x : unit -> int = <fun> it seems like ocaml can infer the return type of the function you declared, which is int.

In Scala, when you declare def x[A](): A you define a function, and let's elaborate what this function means:

  • The name of the function is x.
  • This function is generic, and expects to get a type A.
  • The function has no arguments.
  • The return type is A.

When returning in such a function 8, the compiler tries to cast 8 into an A unsuccessfully.

What you are trying to do is something like:

scala> def x[A](): Int = 8 
x: [A]()Int
Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
1

In Scala it's allowed not to specify return type, then it will be inferred

def x() = 8 
x(): Int

If you do want to specify the return type then, I guess, you can emulate the OCaml behavior of type parameter in Scala with a helper class

val t = TypeOf(8)
def x(): t.A = t.a

case class TypeOf[_A](a: _A) {
  type A = _A
}

or

val t = TypeOf(8)
def x(): t.A = 8

implicit class TypeOf[_A](a: _A) {
  type A = _A
}

or even

def x(): TypeOf.`8`.A = 8

import scala.language.dynamics
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

object TypeOf extends Dynamic {
  def selectDynamic(term: String): Any = macro selectDynamicImpl
  def selectDynamicImpl(c: whitebox.Context)(term: c.Tree): c.Tree = {
    import c.universe._, internal.decorators._
    val q"${termStr: String}" = term
    val termType = c.typecheck(c.parse(termStr), silent = false).tpe
    val resType = c.typecheck(tq"{ type A = $termType }", mode=c.TYPEmode, silent = false).tpe
    q"()".setType(resType)
  }
}

(motivated by shapeless.Witness.selectDynamic).

See also

T <: A, return T method

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66