How do I declare an empty/case-less pattern matching function to satisfy a type definition? I'm happy with the function just throwing a runtime exception if it's ever called.
I'm working through the Scala tutorial for Java programmers, in which I have a working function that performs variable substitution on a mathematic expression presented as a tree given a String => Int map. I want to call the same code path even when no variables should exist (in this case after taking a derivative of the expression), but I can't find a concise way to satisfy the type requirements. Here's the full code I have that works but feels wrong:
abstract class Tree
case class Sum(l: Tree, r: Tree) extends Tree
case class Var(n: String) extends Tree
case class Const(v: Int) extends Tree
object CalculatorPatternsPrime {
def eval(tree: Tree, env: String => Int): Int = tree match {
case Sum(l, r) => eval(l, env) + eval(r, env)
case Var(n) => env(n)
case Const(v) => v
}
def eval(tree: Tree): Int = eval(tree, { case "ignore" => -1 })
def derive(tree: Tree, v: String): Tree = tree match {
case Sum(l, r) => Sum(derive(l, v), derive(r, v))
case Var(n) if (n == v) => Const(1)
case _ => Const(0)
}
def main(args: Array[String]): Unit = {
val env: String => Int = { case "x" => 5 case "y" => 7 }
val tree = Sum(
Sum(Const(7), Var("y")),
Sum(Var("x"), Var("x"))
)
println(eval(tree, env))
println(derive(tree, "x"))
println(eval(derive(tree, "x")))
}
}
As you can see I have a dummy { case "ignore" => -1 } to make the type system happy, and the code works fine but I feel like there must be a better way to do this. Here are two alternatives I've considered:
Just writing out a full method body to eval(tree: Tree) instead of trying to call eval(tree: Tree, env: String => Int) but this duplicates the code handling the Sum and Const cases.
Making env an optional/union type and letting it throw an NPE.
What is the idiomatic approach here?
There are a few ways to go about this:
First, as jwvh noted, you can eliminate the single-argument eval by using a default argument for the second parameter in the two-argument eval.
The question then arises, what should that default argument be?
env is a String => Int, which is shorthand for Function1[-A, +R]: contravariant in the argument type and covariant in the result type. For our purposes, this means that any function which accepts a supertype of String (including String) and results in a subtype of Int (including Int) will work.
Since you've said you're OK with throwing, this is a reasonable default function:
{ a: Any => throw new AssertionError(s"shouldn't have looked up $a in the environment") }
Assuming that some other component of the system is ensuring that if there's a Var expression in the tree passed to eval, there's always an appropriate entry in the environment, this might be the most honest thing to do: something important in your system isn't maintaining an invariant, so trying to reason about it in your system might just make things worse.
That function works because it has the type Any => Nothing, which is a subtype of String => Int: you can pass it a String (String is a subtype of Any) and it will never have a result that's not an Int or a subtype of an Int (it doesn't have a result).
As an alternative, you could also use PartialFunction.empty as a default, which will throw a MatchError if it's ever called. This fits using a partial function literal (which is what a bare { case... } block is).
So I would have either (both have type Env = String => Int)
val emptyEnv: Env = { a: Any => throw new AssertionError(s"shouldn't have looked up $a in the environment") }
or
val emptyEnv: Env = PartialFunction.empty
And then define eval as:
def eval(tree: Tree, env: Env = emptyEnv): Int
As a side note, I would strongly recommend making Tree sealed:
sealed abstract class Tree
Which limits where classes extending Tree can be defined and gives you a stronger guarantee that invariants about Trees are enforced.
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.
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 have this hierarchy of traits and classes in Scala:
trait A
trait B[T] extends A {
def v: T
}
case class C(v:Int) extends B[Int]
case class D(v:String) extends B[String]
val l:List[A] = C(1) :: D("a") :: Nil
l.foreach(t => println(t.asInstanceOf[B[_]].v))
I cannot change the type hierarchy or the type of the list.
Is there a better way to avoid the asInstanceOf[B[_]] statement?
You might try pattern matching.
l.collect{case x :B[_] => println(x.v)}
You might try something like this:
for (x <- l.view; y <- Some(x).collect { case b: B[_] => b }) println(y.v)
It doesn't require any isInstanceOf or asInstanceOf, and never crashes, even if your list contains As that aren't B[_]s. It also doesn't create any lengthy lists as intermediate results, only small short-lived Options.
Not as concise, but also much less surprising solution:
for (x <- l) {
x match {
case b: B[_] => println(b.v)
case _ => /* do nothing */
}
}
If you could change the type of l to List[B[_]], this would be the preferable solution.
I think the most ideomatic way to do it would be to supply B with an extractor object and pattern match for B values:
object B {
def unapply[T](arg: B[T]): Some[T] = Some(arg.v)
}
l.collect{case B(x) => println(x)}
If B is declared in a source file you can't alter you might need a different name for the extractor object.
For a homework assignment I wrote some scala code in which I have the following classes and object (used for modeling a binary tree):
object Tree {
def fold[B](t: Tree, e: B, n: (Int, B, B) => B): B = t match {
case Node(value, l, r) => n(value,fold(l,e,n),fold(r,e,n))
case _ => e
}
def sumTree(t: Tree): Tree =
fold(t, Nil(), (a, b: Tree, c: Tree) => {
val left = b match {
case Node(value, _, _) => value
case _ => 0
}
val right = c match {
case Node(value, _, _) => value
case _ => 0
}
Node(a+left+right,b,c)
})
}
abstract case class Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
case class Nil extends Tree
My question is about the sumTree function which creates a new tree where the nodes have values equal to the sum of the values of its children plus it's own value.
I find it rather ugly looking and I wonder if there is a better way to do this. If I use recursion which works top-down this would be easier, but I could not come up with such a function.
I have to implement the fold function, with a signature as in the code, to calculate sumTree
I got the feeling this can be implemented in a better way, maybe you have suggestions?
First of all, I believe and if I may say so, you've done a very good job. I can suggest a couple of slight changes to your code:
abstract class Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
case object Nil extends Tree
Tree doesn't need to be a case-class, besides using a case-class as non-leaf node is deprecated because of possible erroneous behaviour of automatically generated methods.
Nil is a singleton and best defined as a case-object instead of case-class.
Additionally consider qualifying super class Tree with sealed. sealed tells compiler that the class can only be inherited from within the same source file. This lets compiler emit warnings whenever a following match expression is not exhaustive - in other words doesn't include all possible cases.
sealed abstract class Tree
The next couple of improvement could be made to the sumTree:
def sumTree(t: Tree) = {
// create a helper function to extract Tree value
val nodeValue: Tree=>Int = {
case Node(v,_,_) => v
case _ => 0
}
// parametrise fold with Tree to aid type inference further down the line
fold[Tree](t,Nil,(acc,l,r)=>Node(acc + nodeValue(l) + nodeValue(r) ,l,r))
}
nodeValue helper function can also be defined as (the alternative notation I used above is possible because a sequence of cases in curly braces is treated as a function literal):
def nodeValue (t:Tree) = t match {
case Node(v,_,_) => v
case _ => 0
}
Next little improvement is parametrising fold method with Tree (fold[Tree]). Because Scala type inferer works through the expression sequentially left-to-right telling it early that we're going to deal with Tree's lets us omit type information when defining function literal which is passed to fold further on.
So here is the full code including suggestions:
sealed abstract class Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
case object Nil extends Tree
object Tree {
def fold[B](t: Tree, e: B, n: (Int, B, B) => B): B = t match {
case Node(value, l, r) => n(value,fold(l,e,n),fold(r,e,n))
case _ => e
}
def sumTree(t: Tree) = {
val nodeValue: Tree=>Int = {
case Node(v,_,_) => v
case _ => 0
}
fold[Tree](t,Nil,(acc,l,r)=>Node(acc + nodeValue(l) + nodeValue(r) ,l,r))
}
}
The recursion you came up with is the only possible direction that lets you traverse the tree and produce a modified copy of the immutable data structure. Any leaf nodes have to be created first before being added to the root, because individual nodes of the tree are immutable and all objects necessary to construct a node have to be known before the construction: leaf nodes need to be created before you can create root node.
As Vlad writes, your solution has about the only general shape you can have with such a fold.
Still there is a way to get rid of the node value matching, not only factor it out. And personally I would prefer it that way.
You use match because not every result you get from a recursive fold carries a sum with it. Yes, not every Tree can carry it, Nil has no place for a value, but your fold is not limited to Trees, is it?
So let's have:
case class TreePlus[A](value: A, tree: Tree)
Now we can fold it like this:
def sumTree(t: Tree) = fold[TreePlus[Int]](t, TreePlus(0, Nil), (v, l, r) => {
val sum = v+l.value+r.value
TreePlus(sum, Node(sum, l.tree, r.tree))
}.tree
Of course the TreePlus is not really needed as we have the canonical product Tuple2 in the standard library.
Your solution is probably more efficient (certainly uses less stack), but here's a recursive solution, fwiw
def sum( tree:Tree):Tree ={
tree match{
case Nil =>Nil
case Tree(a, b, c) =>val left = sum(b)
val right = sum(c)
Tree(a+total(left)+total(right), left, right)
}
}
def total(tree:Tree):Int = {
tree match{
case Nil => 0
case Tree(a, _, _) =>a
}
You've probably turned in your homework already, but I think it's still worth pointing out that the way your code (and the code in other people's answers) looks like is a direct result of how you modeled the binary trees. If, instead of using an algebraic data type (Tree, Node, Nil), you had gone with a recursive type definition, you wouldn't have had to use pattern matching to decompose your binary trees. Here's my definition of a binary tree:
case class Tree[A](value: A, left: Option[Tree[A]], right: Option[Tree[A]])
As you can see there's no need for Node or Nil here (the latter is just glorified null anyway - you don't want anything like this in your code, do you?).
With such definition, fold is essentially a one-liner:
def fold[A,B](t: Tree[A], z: B)(op: (A, B, B) => B): B =
op(t.value, t.left map (fold(_, z)(op)) getOrElse z, t.right map (fold(_, z)(op)) getOrElse z)
And sumTree is also short and sweet:
def sumTree(tree: Tree[Int]) = fold(tree, None: Option[Tree[Int]]) { (value, left, right) =>
Some(Tree(value + valueOf(left, 0) + valueOf(right, 0), left , right))
}.get
where valueOf helper is defined as:
def valueOf[A](ot: Option[Tree[A]], df: A): A = ot map (_.value) getOrElse df
No pattern matching needed anywhere - all because of a nice recursive definition of binary trees.
I have this class in Scala:
object Util {
class Tapper[A](tapMe: A) {
def tap(f: A => Unit): A = {
f(tapMe)
tapMe
}
def tap(fs: (A => Unit)*): A = {
fs.foreach(_(tapMe))
tapMe
}
}
implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
}
Now,
"aaa".tap(_.trim)
doesn't compile, giving the error
error: missing parameter type for expanded function ((x$1) => x$1.trim)
Why isn't the type inferred as String? From the error it seems that the implicit conversion does fire (otherwise the error would be along the lines of "tap is not a member of class String"). And it seems the conversion must be to Tapper[String], which means the type of the argument is String => Unit (or (String => Unit)*).
The interesting thing is that if I comment out either of tap definitions, then it does compile.
6.26.3 Overloading Resolution
One first determines the set of
functions that is potentially
applicable based on the shape of the
arguments
...
If there is precisely one alternative
in B, that alternative is chosen.
Otherwise, let S1, . . . , Sm be the
vector of types obtained by typing
each argument with an undefined
expected type.
Both overloads of tap are potentially applicable (based on the 'shape' of the arguments, which accounts for the arity and type constructors FunctionN).
So the typer proceeds as it would with:
val x = _.trim
and fails.
A smarter algorithm could take the least upper bound of the corresponding parameter type of each alternative, and use this as the expected type. But this complexity isn't really worth it, IMO. Overloading has many corner cases, this is but another.
But there is a trick you can use in this case, if you really need an overload that accepts a single parameter:
object Util {
class Tapper[A](tapMe: A) {
def tap(f: A => Unit): A = {
f(tapMe)
tapMe
}
def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = {
(Seq(f0, f1) ++ fs).foreach(_(tapMe))
tapMe
}
}
implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
"".tap(_.toString)
"".tap(_.toString, _.toString)
"".tap(_.toString, _.toString, _.toString)
}