I have a trait that defines an associated type. In a function, I want to return this associated type which is different depending on the supplied value:
sealed trait Abstract {
type T
}
class Impl1 extends Abstract {
type T = Int
}
class Impl2 extends Abstract {
type T = Boolean
}
object G {
def get[A <: Abstract] (x: A): A#T = {
x match {
case i1: Impl1 =>
5
case i2: Impl2 =>
true
}
}
}
The problem is, that scala doesn't recognize that in the matched case A#T is Int or Boolean respectively and I get the following error message:
Expression of type Int doesn't conform to expected type A#T
How can I solve this?
Your match condition gives AnyVal result, it know nothing about A#T. You should use .asInstanceOf[A#T] on result or extrac pattern matching
val z = x match {
case i1: Impl1 =>
5
case i2: Impl2 =>
true
}
z match {
case x: A#T => x
}
The problem is, that scala doesn't recognize that in the matched case A#T is Int or Boolean respectively
Strictly speaking, it isn't. For example, you could call
get[Abstract](new Impl1)
in which case A#T is Abstract#T which is neither Int not Boolean. Or
get(throw new Exception)
so you get Nothing#T (Nothing <: Abstract, after all!).
Generally speaking, in my experience Scala isn't that good even at GADTs, and doing things like that you often have to "help" it by using casts, and it's your responsibility to make sure they are safe.
Related
Suppose Lofty is a sealed trait and Earthy is one of its case classes. In a match such as this:
loftyItem match {
...
case e # Earthy(x,y,z) => { foo(e) }
...
}
where foo expects an Earthy as argument, the compiler melts down because e is inferred only to be of type Lofty. I can work around this, but it doesn't fit my model of how things ought to be. (I'm working with Scala 2.13.5.) Can someone explain why the compiler's behavior makes sense, and make me happy with Scala again?
In response to comments, let me be more precise:
object QTest {
trait Ethereal
case class Lofty[A <: Ethereal](val slotMaybe: Option[A]) {
}
class Earthy(val field: Int) extends Ethereal
object Earthy {
def apply(fx: Int): Earthy = {
new Earthy(fx)
}
def unapply(x: Ethereal): Option[Int] = x match {
case y: Earthy => Some(y.field)
case _ => None
}
}
def testReally(just: Lofty[Ethereal]):
Lofty[Earthy] = {
just.slotMaybe match {
case Some(c) => c match {
case cnfC # Earthy(i) => {
just.copy(slotMaybe = Some(cnfC))
}
case _ => throw new RuntimeException("oops")
}
case _ => throw new RuntimeException("oops")
}
}
}
Compiling this produces the error:
QTest.scala:25: error: type mismatch;
found : QTest.Ethereal
required: QTest.Earthy
just.copy(slotMaybe = Some(cnfC))
I obviously jumped to a conclusion, but the full example seems to have the same issue. Why does the compiler infer type Ethereal for cnfC instead of Earthy? Even if the compiler gets the type right for most uses of #, why does it get it wrong here?
SLS 8.1.3 Pattern Binders states
A pattern p implies a type T if the pattern matches only values of the type T.
The pattern Earthy(i) in
case cnfC # Earthy(i) =>
represents extractor pattern meaning it will match according to your definition of unapply which is
object Earthy {
def unapply(x: Ethereal): Option[Int] = ???
}
Because the declared type of x is wider Ethereal instead of narrower Earthy it will not match
... only values of the type T
where T = Earthy, but instead it can match other subtypes of Ethereal as well. Hence the compiler can only be sure it will be some Ethereal.
If you wish to make it compile with the extractor pattern then either declare your unapply as
object Earthy {
def unapply(x: Earthy): Option[Int] = ???
}
or better yet use case class instead which gets a correct unapply automagically
case class Earthy(field: Int) extends Ethereal
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
// Start writing your ScalaFiddle code here
sealed trait DSL[A]{
// def run(): A ={
// this match {
// case GetLength(something) =>
// something.length
// case ShowResult(number) =>
// s"the length is $number"
// }
// }
}
case class GetLength(something: String) extends DSL[Int]
case class ShowResult(number: Int) extends DSL[String]
def run[A](fa:DSL[A]): A ={
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
val dslGetLength = GetLength("123456789")
val length = run(dslGetLength)
val dslShowResult = ShowResult(length)
println(run(dslShowResult))
// print: the length is 9
scalafiddle here
why does the run function not compile in the DSL[A] trait, but worked outside?
how does type inference work in this case?
This is a case of generalized abstract data type.
When you have a DSL[A] and function returning A, compiler can prove that:
for case GetLength A=Int so you can return Int there
for case ShowResult A=String so you can return String
however, Scala 2 is known to not have a perfect support of GADTs, so sometimes compiler fails, even if it should work. I guess some compiler dev could figure out the exact case, but, interestingly, it can be worked around with:
sealed trait DSL[A]{
def run(): A = DSL.run(this)
}
object DSL {
def run[A](fa:DSL[A]): A ={
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
case class GetLength(something: String) extends DSL[Int]
case class ShowResult(number: Int) extends DSL[String]
My wild guess would be that pattern matching in a generic method is kind of a special case in a compiler, which is not triggered when A is fixed. I think that, because the following code also works:
sealed trait DSL[A]{
def run(): A = runMe(this)
private def runMe[B](dsl: DSL[B]): B = {
dsl match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
whereas this fails as well:
sealed trait DSL[A]{
def run(): A = {
val fa: DSL[A] = this // make sure error is not related to special treatment of "this", this.type, etc
fa match {
case GetLength(something) =>
something.length
case ShowResult(number) =>
s"the length is $number"
}
}
}
cmd4.sc:5: constructor cannot be instantiated to expected type;
found : ammonite.$sess.cmd4.GetLength
required: ammonite.$sess.cmd4.DSL[A]
case GetLength(something) =>
^
cmd4.sc:7: constructor cannot be instantiated to expected type;
found : ammonite.$sess.cmd4.ShowResult
required: ammonite.$sess.cmd4.DSL[A]
case ShowResult(number) =>
^
Compilation Failed
In other words, I suspect that type parameter change how things are being evaluated:
def runMe[B](dsl: DSL[B]): B has a type parameter, so results of each case inside match are compared against B where for each case value of B can be proven to be some specific type (Int, String)
in def run: A however compiler is somehow prevented from making such analysis - IMHO it is a bug, but perhaps it is a result of some obscure feature.
From what I see the same error occurs in Dotty, so it is either duplicated bug or a limitation of a type-level checker (after all GADT aren't widely use din Scala, yet) - I would suggest reporting issue to Scala/Dotty team and letting them decide what it is.
Pattern matching seems to work differently depending on if type parameter comes from the enclosing method versus enclosing class. Here is a simplified example where using class type parameter A
trait Base[T]
case class Derived(v: Int) extends Base[Int]
class Test[A] {
def method(arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
raises error
Error:(7, 12) constructor cannot be instantiated to expected type;
found : A$A87.this.Derived
required: A$A87.this.Base[A]
case Derived(_) => 42
^
whilst it works using method type parameter A
class Test {
def method[A](arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
SLS 8.4: Pattern Matching Expressions seems to explain what happens in the method scenario
Let π be the type of the selector expression π and let π1,β¦,ππ be
the type parameters of all methods enclosing the pattern matching
expression. For every ππ, let πΏπ be its lower bound and ππ be
its higher bound. Every pattern πβπ1,,β¦,ππ can be typed in two
ways. First, it is attempted to type π with π as its expected type.
If this fails, π is instead typed with a modified expected type πβ²
which results from π by replacing every occurrence of a type
parameter ππ by undefined.
AFAIU, we have
e = arg
a1 = A
T = Base[A]
p1 = Derived(_)
First, it attempts to type π with π as its expected type, however Derived does not conform to Base[A]. Thus it attempts the second rule
If this fails, π is instead typed with a modified expected type πβ²
which results from π by replacing every occurrence of a type
parameter ππ by undefined.
Assuming undefined means something like existential type, then we have T' = Base[_], and because the following indeed holds
implicitly[Derived <:< Base[_]]
then pattern matching in the case of method type parameter becomes something like
class Test {
def method[A](arg: Base[A]) = {
(arg: Base[_]) match {
case Derived(_) => 42
}
}
}
which indeed compiles. This seems to be confirmed by making the class type parameter case successfully compile like so
class Test[A] {
def method(arg: Base[A]) = {
(arg: Base[_]) match {
case Derived(_) => 42
}
}
}
Therefore it seems the second rule is not attempted in type parameter inference for constructor patterns when type parameter comes from enclosing class.
At least these seems to be some of the moving pieces which hopefully someone with actual knowledge can assemble into coherent explanation, as I am mostly guessing.
run returns a generic A, but this only works in the case you didn't comment because you're accepting A as a type parameter (which the compiler can figure out).
The following would work instead:
sealed trait DSL[A] {
def run(): A
}
case class GetLength(something: String) extends DSL[Int] {
def run(): Int = something.length
}
case class ShowResult(number: Int) extends DSL[String] {
def run(): String = s"the length is $number"
}
val dslGetLength = GetLength("123456789")
val length = dslGetLength.run()
val dslShowResult = ShowResult(length)
println(dslShowResult.run())
You can play around with this code here on Scastie or alternatively on Scala Fiddle.
This code compiles with an error:
def f1[T](e: T): T = e match {
case i:Int => i
case b:Boolean => b
}
// type mismatch;
// found : i.type (with underlying type Int)
// required: T
// case i:Int => i ...
And this code implementing GADT looks pretty identical from type checking perspective, but compiles without error:
sealed trait Expr[T]
case class IntExpr(i: Int) extends Expr[Int]
case class BoolExpr(b: Boolean) extends Expr[Boolean]
def eval[T](e: Expr[T]): T = e match {
case IntExpr(i) => i
case BoolExpr(b) => b
}
In both cases inside pattern matching expression we know that i and b are Int and Boolean. Why compilation failed on first example and succeeded on the second one?
The first case is unsound because you underestimate the variety of types in Scala type system. It would make sense if, when we took case i:Int branch we knew T was Int, or at least a supertype of Int. But it doesn't have to be! E.g. it could be 42.type or a tagged type.
There's no such problem in the second case, because from IntExpr <: Expr[T], the compiler does know T must be exactly Int.
You ask of your function to return a type T, then you pattern-match against Int and Boolean.
Except your function has no evidence that Int and Boolean are also of type T: when you pattern-match, you introduce the constraint that Int <: T and Boolean <: T.
You could either replace the return type T by a fixed type like String and return a String, or introduce a constraint that will satisfy both the case Int and Boolean.
//this compiles
def f1[T](e: T ): String = e match {
case _:Int => "integer"
case _:Boolean => "boolean"
}
//this compiles too, but will return AnyVal
def f1[T >: AnyVal](e: T ): T = e match {
case i:Int => i
case b:Boolean => b
}
Basically you can't just return any type T dynamically because you need to prove at compile time that your function type-checks out.
The other function in your example avoids the issue by encapsulating type constraints within case classes IntExpr <: Expr[Int] and BoolExpr <: Expr[Boolean] (notice how Expr[_] would be the equivalent of T in the constraints I mentioned above). At compile time, T is properly identified in all cases (e.g in the IntExpr you know it's an Int)
In addition to #Esardes answer, this worked by defining a type bound for T:
scala> def f1[T >: AnyVal](e: T):T = e match {
| case i:Int => i
| case b:Boolean => b
| }
f1: [T >: AnyVal](e: T)T
scala> f1(1)
res3: AnyVal = 1
scala> f1(true)
res4: AnyVal = true
Type Driven Development with Idris presents this program:
StringOrInt : Bool -> Type
StringOrInt x = case x of
True => Int
False => String
How can such a method be written in Scala?
Alexey's answer is good, but I think we can get to a more generalizable Scala rendering of this function if we embed it in a slightly larger context.
Edwin shows a use of StringOrInt in the function valToString,
valToString : (x : Bool) -> StringOrInt x -> String
valToString x val = case x of
True => cast val
False => val
In words, valToString takes a Bool first argument which fixes the type of its second argument as either Int or String and renders the latter as a String as appropriate for its type.
We can translate this to Scala as follows,
sealed trait Bool
case object True extends Bool
case object False extends Bool
sealed trait StringOrInt[B, T] {
def apply(t: T): StringOrIntValue[T]
}
object StringOrInt {
implicit val trueInt: StringOrInt[True.type, Int] =
new StringOrInt[True.type, Int] {
def apply(t: Int) = I(t)
}
implicit val falseString: StringOrInt[False.type, String] =
new StringOrInt[False.type, String] {
def apply(t: String) = S(t)
}
}
sealed trait StringOrIntValue[T]
case class S(s: String) extends StringOrIntValue[String]
case class I(i: Int) extends StringOrIntValue[Int]
def valToString[T](x: Bool)(v: T)(implicit si: StringOrInt[x.type, T]): String =
si(v) match {
case S(s) => s
case I(i) => i.toString
}
Here we are using a variety of Scala's limited dependently typed features to encode Idris's full-spectrum dependent types.
We use the singleton types True.type and False.type to cross from the value level to the type level.
We encode the function StringOrInt as a type class indexed by the singleton Bool types, each case of the Idris function being represented by a distinct implicit instance.
We write valToString as a Scala dependent method, allowing us to use the singleton type of the Bool argument x to select the implicit StringOrInt instance si, which in turn determines the type parameter T which fixes the type of the second argument v.
We encode the dependent pattern matching in the Idris valToString by using the selected StringOrInt instance to lift the v argument into a Scala GADT which allows the Scala pattern match to refine the type of v on the RHS of the cases.
Exercising this on the Scala REPL,
scala> valToString(True)(23)
res0: String = 23
scala> valToString(False)("foo")
res1: String = foo
Lots of hoops to jump through, and lots of accidental complexity, nevertheless, it can be done.
This would be one approach (but it's a lot more limited than in Idris):
trait Type {
type T
}
def stringOrInt(x: Boolean) = // Scala infers Type { type T >: Int with String }
if (x) new Type { type T = Int } else new Type { type T = String }
and then to use it
def f(t: Type): t.T = ...
If you are willing to limit yourself to literals, you could use singleton types of true and false using Shapeless. From examples:
import syntax.singleton._
val wTrue = Witness(true)
type True = wTrue.T
val wFalse = Witness(false)
type False = wFalse.T
trait Type1[A] { type T }
implicit val Type1True: Type1[True] = new Type1[True] { type T = Int }
implicit val Type1False: Type1[False] = new Type1[False] { type T = String }
See also Any reason why scala does not explicitly support dependent types?, http://www.infoq.com/presentations/scala-idris and http://wheaties.github.io/Presentations/Scala-Dep-Types/dependent-types.html.
I noticed that getStringOrInt example has not been implemented by either of the two answers
getStringOrInt : (x : Bool) -> StringOrInt x
getStringOrInt x = case x of
True => 10
False => "Hello"
I found Miles Sabin explanation very useful and this approach is based on his.
I found it more intuitive to split GADT-like construction from Scala apply() trickery and try to map my code to Idris/Haskell concepts. I hope others find this useful. I am using GADT explicitly in names to emphasize the GADT-ness.
The 2 ingredients of this code are: GADT concept and Scala's implicits.
Here is a slightly modified Miles Sabin's solution that implements both getStringOrInt and valToString.
sealed trait StringOrIntGADT[T]
case class S(s: String) extends StringOrIntGADT[String]
case class I(i: Int) extends StringOrIntGADT[Int]
/* this compiles with 2.12.6
before I would have to use ah-hoc polymorphic extract method */
def extractStringOrInt[T](t: StringOrIntGADT[T]) : T =
t match {
case S(s) => s
case I(i) => i
}
/* apply trickery gives T -> StringOrIntGADT[T] conversion */
sealed trait EncodeInStringOrInt[T] {
def apply(t: T): StringOrIntGADT[T]
}
object EncodeInStringOrInt {
implicit val encodeString : EncodeInStringOrInt[String] = new EncodeInStringOrInt[String]{
def apply(t: String) = S(t)
}
implicit val encodeInt : EncodeInStringOrInt[Int] = new EncodeInStringOrInt[Int]{
def apply(t: Int) = I(t)
}
}
/* Subtyping provides type level Boolean */
sealed trait Bool
case object True extends Bool
case object False extends Bool
/* Type level mapping between Bool and String/Int types.
Somewhat mimicking type family concept in type theory or Haskell */
sealed trait DecideStringOrIntGADT[B, T]
case object PickS extends DecideStringOrIntGADT[False.type, String]
case object PickI extends DecideStringOrIntGADT[True.type, Int]
object DecideStringOrIntGADT {
implicit val trueInt: DecideStringOrIntGADT[True.type, Int] = PickI
implicit val falseString: DecideStringOrIntGADT[False.type, String] = PickS
}
All this work allows me to implement decent versions of getStringOrInt and valToString
def pickStringOrInt[B, T](c: DecideStringOrIntGADT[B, T]): StringOrIntGADT[T]=
c match {
case PickS => S("Hello")
case PickI => I(2)
}
def getStringOrInt[T](b: Bool)(implicit ev: DecideStringOrIntGADT[b.type, T]): T =
extractStringOrInt(pickStringOrInt(ev))
def valToString[T](b: Bool)(v: T)(implicit ev: EncodeInStringOrInt[T], de: DecideStringOrIntGADT[b.type, T]): String =
ev(v) match {
case S(s) => s
case I(i) => i.toString
}
All this (unfortunate) complexity appears to be needed, for example this will not compile
// def getStringOrInt2[T](b: Bool)(implicit ev: DecideStringOrIntGADT[b.type, T]): T =
// ev match {
// case PickS => "Hello"
// case PickI => 2
// }
I have a pet project in which I compared all code in the Idris book to Haskell.
https://github.com/rpeszek/IdrisTddNotes/wiki
(I am starting work on a Scala version of this comparison.)
With type-level Boolean (which is effectively what we have here) StringOrInt examples become super simple if we have type families (partial functions between types). See bottom of
https://github.com/rpeszek/IdrisTddNotes/wiki/Part1_Sec1_4_5
This makes Haskell/Idris code is so much simpler and easier to read and understand.
Notice that valToString matches on StringOrIntGADT[T]/StringOrIntValue[T] constructors and not directly on Bool. That is one example where Idris and Haskell shine.
Consider the following code:
trait Trait {
type X
type Y[_ <: X]
}
type T[X0,Y0[_ <: X0]] = Trait { type X = X0; type Y[S <: X0] = Y0[S] }
class TraitImpl extends Trait {
override type X = Int
override type Y[I <: Int] = List[I]
}
case class Wrap[X,Y[_ <: X]](t: T[X,Y])
val wrap = new Wrap[Int,List](new TraitImpl) // why explicitly write types?
Why explicitly write types, see my other question.
Now I want to pattern match on wrap:
wrap match {
case w: Wrap[_,_] => // error
case w: Wrap[_,_[_]] => // error
case Wrap(t) => // error
case w: Wrap[Int,List] => // fine
}
I am used to have several choices for pattern matching, in particular the last two case statements, sometimes the first one.
But none of the first three cases are working, why? (especially case Wrap(t) =>)?