I have an enum defined as follows
enum Operator[T](val expr: String, val tag: T):
case Plus(override val tag: T) extends Operator("+", tag)
case Minus(override val tag: T) extends Operator("-", tag)
case Multiply(override val tag: T) extends Operator("*", tag)
Next is a function call that checks whether the passed list of operators starts with a certain operator or not
def extract[A](expect: Operator[A], operators: List[Operator[A]]): Either[Error[A], (Operator[A], List[Operator[A]])] =
operators match {
case `expect` :: tail => Right(expect, operators.tail)
case _ =>
expect match {
case Plus(_) => Left(Error.ExpectPlus(operators))
case Minus(_) => Left(Error.ExpectMinus(operators))
case Multiply(_) => Left(Error.ExpectMultiply(operators))
}
}
But when I call the function with the following parameter, I get the error as below
extract(Plus(_), operators)
^^^^^^^^^^^^^^^
[error] | Found: Any => Operator[Any]
[error] | Required: Operator[Any]
Can anyone help me explain why using _ as a parameter is interpreted as Any => [Any], is there any way to solve this error?
Here is the snippet: https://scastie.scala-lang.org/XLrcrUBvSneJs60w87k43A
What you wrote is the same as below:
extract(x => Plus(x), operators)
// Same as:
extract(Plus(_), operators)
Meaning Plus(_) defines a function, which is not what extract expects.
This is actually a complement to #Luis Miguel MejΓa SuΓ‘rez's comment and #GaΓ«l J's answer. As stated earlier in the comments and answers, you're actually passing a function, not an Operator instance to your extract function. I think what you intended to do, was to use a wildcard for the tag in Plus (of course this is not a wildcard, this results in a function). And based on these assumptions, I think what you're looking for is type matching. So this needs a little bit of refactoring, now this code is written in Scala 2, but the concepts should be pretty much applicable to Scala 3 as well:
// I have defined the Operator as sealed abstract class earlier, and all the subtypes
sealed trait Err[OP <: Operator[_]]
object Err {
def apply[T <: Operator[_]]: Err[T] = new Err[T] {} // In Scala3 you can easily do type matching here
}
import scala.reflect.ClassTag
def extract[A, OperatorType <: Operator[A] : ClassTag](operators: List[Operator[A]]): Either[Err[OperatorType], (Operator[A], List[Operator[A]])] = {
operators match {
case (head : OperatorType) :: tail =>
Right(head, tail)
case _ => Left(Err[OperatorType])
}
}
val ops: List[Operator[Int]] = List(Plus(2), Minus(4), Multiply(7))
val shouldFail = extract[Int, Minus[Int]](ops) // fails since first element is not of type Minus
val shouldSucceed = extract[Int, Plus[Int]](ops) // succeeds
On scastie.
Related
I'm not very experienced with using Scala and I think I'm missing something when it comes to implicits. I'm essentially trying to take an enumerated argument that represents a certain logical operator (e.g. representing greater than '>' as the enumerated value Greater) and return a function that will apply that operator to a left and right operand and return the boolean result.
So, for example, if I had something like the following:
val gt = LogicalOperator[Int](Greater)
then gt(2, 1) would return True. I'd also like this to work for the == and != operators as well but additionally extended to iterables (e.g. eq(List(1,2,3), List(1,2,3)) returns True).
So basically I want to be able to generate comparative functions for a generic type parameter of Ordering or Iterable and be able to pass the function around to other methods and/or objects. I've been trying to use Typeclasses with implicits but I'm running into issues with using traits such as Ordering as type parameters.
Here is basically what I have so far. Started with the enumerated operators:
object LogicalOperation extends Enumeration {
type LogicalOperation = Value
val Greater, GreaterEqual, Less, LessEqual, Equal, NotEqual = Value
def fromName(s: String): Value = values.find(_.toString == s).get
}
Then trying to create some implicit operator factories:
sealed trait OperatorFactory[T] {
def apply(operation: LogicalOperation): (T, T) => Boolean
}
object OperatorFactory {
implicit class OrderedOperator[T: Ordering](operation: LogicalOperation)(implicit ord: Ordering[T]) extends OperatorFactory[T] {
def apply(operation: LogicalOperation): (T, T) => Boolean = {
operation match {
case Greater => ord.gt
case GreaterEqual => ord.gteq
case Less => ord.lt
case LessEqual => ord.lteq
case Equal => ord.equiv
case NotEqual => (l: T, r: T) => !ord.equiv(l, r)
}
}
}
implicit class IterableOperator[Iterable](operation: LogicalOperation) extends OperatorFactory[Iterable] {
def apply(operation: LogicalOperation): (Iterable, Iterable) => Boolean = {
operation match {
case Equal => (l: Iterable, r: Iterable) => l.equals(r)
case NotEqual => (l: Iterable, r: Iterable) => !l.equals(r)
case _ => throw new IllegalArgumentException(s"Logical operation ${operation} not applicable for Iterables.")
}
}
}
}
Then creating an operator object to implicitly select the right factory and return the operator function (I think this is part of what is not correct):
object LogicalOperator {
implicit def OrderedOperator[T: Ordering](operation: LogicalOperation) = OperatorFactory.OrderedOperator[T](operation)
implicit def IterOperator[Iterable](operation: LogicalOperation) = OperatorFactory.IterableOperator[Iterable](operation)
def apply[T](operation: LogicalOperation)(implicit operatorFactory: OperatorFactory[T]): (T, T) => Boolean = {
operatorFactory.apply(operation)
}
}
Now, I'm running into a couple of compile errors. If I try to instantiate one of these operator functions as follows:
val gt = LogicalOperator[Int](Greater)
I get basically the following error:
could not find implicit value for parameter operatorFactory: OperatorFactory[Int]
Edit: Tim's answer helped correct this issues:
If I remove that line of code, then I get errors on the implicit classes of the OperatorFactory:
class OrderedOperator needs to be abstract, since method apply in trait OperatorFactory of type [T](operation: LogicalOperation.LogicalOperation)(T, T) => Boolean is not defined
So I think there are issues both with how I'm trying to implicitly instantiate the OperatorFactory and with how the abstract methods for the operator factory are implemented.
I'm also wondering if there is just a simpler way to instantiate and pass around these comparative functions.
sealed trait OperatorFactory[T] {
def apply[T](operation: LogicalOperation): (T, T) => Boolean
}
This code has two type parameters called T. The first is the type parameter of the trait, and the second is the type parameter of apply. You probably want to use the same type in both places, so remove the type parameter from apply:
sealed trait OperatorFactory[T] {
def apply(operation: LogicalOperation): (T, T) => Boolean
}
Enabling the compiler option -Xlint:type-parameter-shadow should generate a warning for this.
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.
I am trying to write a scala function to reverse a list. However, IDEA is highlighting the (warning) line saying "fruitless type test: a value of type ListDefinition.List[T] cannot also be a::[B]"
object ListDefinition {
def reverseList[T](list: List[T]) : List[T] = list match {
(warning) case x :: EmptyList => // something
case _ => // do somehting
}
abstract class List[+T]
case object EmptyList extends List[Nothing]
case class ConsList[T](value: T, next: List[T]) extends List[T]
The case statement needs to use the Cons constructor for your list:
case ConsList(x, EmptyList) => // something
The :: constructor is for Scala lists and will not work for yours.
Let's suppose we have a generic class Container:
case class Container[+A](value: A)
We then want to pattern match a Container with a Double and a Container of Any:
val double = Container(3.3)
var container: Container[Any] = double
To do this, we would normally write:
container match {
case c: Container[String] => println(c.value.toUpperCase)
case c: Container[Double] => println(math.sqrt(c.value))
case _ => println("_")
}
However, the compiler gives two warnings, one for each of the first two cases. For example, the first warning says: "non-variable type argument String in type pattern Container[String] is unchecked since it is eliminated by erasure". Because of the erasure, it is impossible during runtime to distinguish between different kinds of containers and the first catch will be matched. As a consequence, container of type Container[Double] will be matched by the first case, which catches Container[String] objects, so toUpperCase method will be called on a Double and a java.lang.ClassCastException will be thrown.
How to match a Container parametrized by a particular type?
Maybe this will help
def matchContainer[A: Manifest](c: Container[A]) = c match {
case c: Container[String] if manifest <:< manifest[String] => println(c.value.toUpperCase)
case c: Container[Double] if manifest <:< manifest[Double] => println(math.sqrt(c.value))
case c: Container[_] => println("other")
}
Edit:
As Impredicative pointed out, Manifest is deprecated. Instead you could do the following:
import reflect.runtime.universe._
def matchContainer[A: TypeTag](c: Container[A]) = c match {
case c: Container[String] if typeOf[A] <:< typeOf[String] => println("string: " + c.value.toUpperCase)
case c: Container[Double] if typeOf[A] <:< typeOf[Double] => println("double" + math.sqrt(c.value))
case c: Container[_] => println("other")
}
In general rarry's answer is correct, for your case however it can be simplified, because your container only contains a single value of a generic type, so you can match on that value's type directly:
container match {
case Container(x: String) => println("string")
case Container(x: Double) => println("double")
case _ => println("w00t")
}
A possible workaround for this could be to use isInstanceOf and asInstanceOf.
container match {
case Container(x) if x.isInstanceOf[String] =>
println(x.asInstanceOf[String].toUpperCase)
case Container(x) if x.isInstanceOf[Double] =>
println(math.sqrt(x.asInstanceOf[Double]))
case _ => println("_")
}
This works, but it doesn't look elegant at all. Professor Martin Odersky, the creator of Scala, says that isInstanceOf and asInstanceOf should be avoided.
As Rob Norris pointed me out, on the forum of the course "Functional programming in Scala" from Coursera, matching by type is a bad practice: case foo: Bar => .... Scala encourages to take advantage of static typing and avoid checking type during runtime. This is consistent with the philosophy of Haskell/ML world. Instead of matching types, case clauses should match constructors.
To solve the Container matching problem, a special container for each type can be defined:
class Container[+A](val value: A)
case class StringContainer(override val value: String)
extends Container(value)
case class DoubleContainer(override val value: Double)
extends Container(value)
And now constructors will be matched, not types:
container match {
case StringContainer(x) => println(x.toUpperCase)
case DoubleContainer(x) => println(math.sqrt(x))
case _ => println("_")
}
Apparently, we could be defined unapply methods in two objects, StringContainer and DoubleContainer and use the same match as above, instead of extending the Container class:
case class Container[+A](val value: A)
object StringContainer {
def unapply(c: Container[String]): Option[String] = Some(c.value)
}
object DoubleContainer {
def unapply(c: Container[Double]): Option[Double] = Some(c.value)
}
But this does not work, again, because of JVM type erasure.
A reference to Rob Norris post, which lead me to this answer can be found here: https://class.coursera.org/progfun-002/forum/thread?thread_id=842#post-3567 . Unfortunately, you can't access it unless you are enrolled in the Coursera course.
Note: you also have an alternative with Miles Sabin's Shapeless library (already mentioned by Miles in 2012 here).
You can see an example in "Ways to pattern match generic types in Scala" from Jaakko Pallari
Typeable is a type class that provides the ability to cast values from Any type to a specific type.
The result of the casting operation is an Option where the Some value will contain the successfully casted value, and the None value represents a cast failure.
TypeCase bridges Typeable and pattern matching. It's essentially an extractor for Typeable instances
import shapeless._
def extractCollection[T: Typeable](a: Any): Option[Iterable[T]] = {
val list = TypeCase[List[T]]
val set = TypeCase[Set[T]]
a match {
case list(l) => Some(l)
case set(s) => Some(s)
case _ => None
}
}
val l1: Any = List(1, 2, 3)
val l2: Any = List[Int]()
val s: Any = Set(1, 2, 3)
extractCollection[Int](l1) // Some(List(1, 2, 3))
extractCollection[Int](s) // Some(Set(1, 2, 3))
extractCollection[String](l1) // None
extractCollection[String](s) // None
extractCollection[String](l2) // Some(List()) // Shouldn't this be None? We'll get back to this.
While Typeable may look like it has what it takes to solve type erasure, it's still subject to the same behaviour as any other runtime code.
This can be seen in the last lines of the previous code examples where empty lists were recognized as string lists even when they were specified to be integer lists. This is because Typeable casts are based on the values of the list. If the list is empty, then naturally that is a valid string list and a valid integer list (or any other list for that matter)