N'importe quel type peut être suivie par un {}
clos de la séquence du type et de l'abstrait non-membre de type définitions. Ceci est connu comme un "raffinement" et est utilisé pour fournir plus de précision sur le type de base est en train d'être affinés. Dans la pratique, les améliorations sont les plus couramment utilisés pour exprimer des contraintes sur le type abstrait des membres de la type raffinés.
C'est un fait peu connu que cette séquence peut être vide, et dans la forme que vous pouvez voir dans la forme de code source, T {}
est le type T
avec un vide de raffinement. Vide le raffinement est ... vide ... donc ne pas ajouter des contraintes supplémentaires à l'raffiné type et par conséquent les types T
et T {}
sont équivalentes. Nous pouvons obtenir la Scala compilateur de vérifier que, pour nous, comme si,
scala> implicitly[Int =:= Int {}]
res0: =:=[Int,Int] = <function1>
Alors, pourquoi ferais-je une telle apparemment inutiles chose en informe? C'est en raison de l'interaction entre la présence de raffinements et de l'inférence de type. Si vous regardez dans la section pertinente de la Scala de Spécification de Langage, vous verrez que l'algorithme d'inférence de types tente d'éviter d'inférer singleton types, au moins dans certaines circonstances. Voici un exemple de ce que fait,
scala> class Foo ; val foo = new Foo
defined class Foo
foo: Foo = Foo@8bd1b6a
scala> val f1 = foo
f1: Foo = Foo@8bd1b6a
scala> val f2: foo.type = foo
f2: foo.type = Foo@8bd1b6a
Comme vous pouvez le voir à partir de la définition de l' f2
de la Scala compilateur sait que la valeur foo
a la plus précise du type foo.type
(ie. le singleton type d' val foo
), cependant, sauf s'il est explicitement demandé de ne pas en déduire que plus précise du type. Au lieu de cela, il déduit la non-singleton (c'est à dire. élargi) type Foo
comme vous pouvez le voir dans le cas de l' f1
.
Mais dans le cas d' Witness
en informe, j'ai explicitement voulez le singleton de type déduire pour l'utilisation de l' value
membre (le point de l'ensemble de l' Witness
est de nous permettre de passer entre le type et la valeur des niveaux via singleton types), donc est-il possible que le compilateur Scala peuvent être incités à le faire?
Il s'avère qu'un vide de raffinement est exactement ce que fait,
scala> def narrow[T <: AnyRef](t: T): t.type = t
narrow: [T <: AnyRef](t: T)t.type
scala> val s1 = narrow("foo") // Widened
s1: String = foo
scala> def narrow[T <: AnyRef](t: T): t.type {} = t // Note empty refinement
narrow: [T <: AnyRef](t: T)t.type
scala> val s2 = narrow("foo") // Not widened
s2: String("foo") = foo
Comme vous pouvez le voir ci-dessus REPL transcription, dans le premier cas s1
a été tapé comme l'élargissement de type String
alors qu' s2
a été attribuée le singleton de type String("foo")
.
Est-ce mandaté par le SLS? Non, mais il est cohérent avec lui, et il fait une sorte de sens. Beaucoup de Scala est l'inférence de type mécanique de mise en œuvre définies plutôt que de spec ed et c'est probablement l'une des moins surprenant et problématique les instances de cette.