I'm doing an exercise in "Scala for the Impatient", Chapter 14, Q8:
Essentially I need to create a function (utilizing pattern matching) that takes in an operator and nodes, and it outputs the result of the operation. E.G. Node(+, Node(*, Leaf(2), Leaf(3)) Leaf(1)) should output 7.
Here are some of the given classes:
sealed abstract class BinaryTree
case class Leaf(value: Int) extends BinaryTree
So I create a Node class, but I'm having difficulties figuring out how to pass in the operator.
case class Node(op: Function (what goes here?) , leaves: BinaryTree*) extends BinaryTree
I want to use pattern matching like so:
tree match {
case Node(op, leaves # _*) => op match {
case op : Function => leaves.reduceLeft(_ op _)
}
case leaf: Leaf => leaf.value
But the
case op : Function => leaves.reduceLeft(_ op _)
part is wrong. I don't know how to use the operator that's being passed in the Node class. What am I doing wrong here?
I am assuming the operator will be always going to be binary hence, our So called BinaryTree will have atleast two operands :
trait BinaryTree
case class Leaf(value: Int) extends BinaryTree
case class Node(op: Function2[Int, Int, Int], l1: BinaryTree*) extends BinaryTree
object Operators {
val + = (a: Int, b: Int) => a + b
val * = (a: Int, b: Int) => a * b
}
def f(tree: BinaryTree): Int = {
tree match {
case n: Node => n.l1.map(f).reduceLeft((r,c) => n.op(r,c))
case leaf: Leaf => leaf.value
}
}
Some test results:
Simple one:
scala> f(Node(Operators.*,Leaf(4),Leaf(2),Leaf(3)))
res4: Int = 24
scala> f(Node(Operators.+,
Node(Operators.*, Leaf(2), Leaf(1), Leaf(4), Leaf(5)), Leaf(6)))
res5: Int = 46
scala> f(Node(Operators.+,
Node(Operators.*, Leaf(2), Leaf(1), Leaf(4), Leaf(5)),
Node(Operators.+,Leaf(9),Leaf(9)), Leaf(6)))
res6: Int = 64
Quite complex:
scala> f(Node(Operators.+,
Node(Operators.*, Leaf(2), Leaf(1),
Node(Operators.* ,Leaf(4), Leaf(5) ,Leaf(2))),
Node(Operators.+,Leaf(9),Leaf(9)), Leaf(6),
Node(Operators.*, Leaf(2), Leaf(2))))
res7: Int = 108
It has more elegant solutions, but since you want it with pattern matching:
sealed abstract class BinaryTree
case class Leaf(value: Int) extends BinaryTree
case class Node(op: (Int, Int) => Int , leaves: BinaryTree*) extends BinaryTree
def calc(tree: BinaryTree): Int = tree match {
case Leaf(v) => v
case Node(op, h, leaves # _*) => leaves.foldLeft(calc(h))((a,b) => op(a,calc(b)))
}
object Operators {
def +(a: Int, b: Int): Int = a + b
def *(a: Int, b: Int): Int = a * b
}
val tree = Node(Operators.+, Node(Operators.*, Leaf(9), Leaf(3)), Leaf(1))
calc(tree)
Related
I would like to define a State that builds a concrete subtype of a trait, as per decodeFoo:
sealed trait Foo
case class Bar(s: String) extends Foo
case class Baz(i: Int) extends Foo
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => sys.error(s"Insufficient data remains to parse int")
}
def bytes(len: Int): State[Seq[Byte], Seq[Byte]] = State[Seq[Byte], Seq[Byte]] {
case bs if bs.length >= len => bs.drop(len) -> bs.take(len)
case _ => sys.error(s"Insufficient data remains to parse $len bytes")
}
val bytes: State[Seq[Byte], Seq[Byte]] = for {
len <- int
bs <- bytes(len)
} yield bs
val string: State[Seq[Byte], String] = bytes.map(_.toArray).map(new String(_, Charset.forName("UTF-8")))
val decodeBar: State[Seq[Byte], Bar] = string.map(Bar)
val decodeBaz: State[Seq[Byte], Baz] = int.map(Baz)
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar
case 1 => decodeBaz
}
This will not compile as State is defined in cats as type State[S, A] and the compiler responds:
Error:(36, 15) type mismatch;
found : cats.data.State[Seq[Byte],FooBarBaz.this.Bar]
(which expands to) cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Bar]
required: cats.data.IndexedStateT[cats.Eval,Seq[Byte],Seq[Byte],FooBarBaz.this.Foo]
Note: FooBarBaz.this.Bar <: FooBarBaz.this.Foo, but class IndexedStateT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
case 0 => decodeBar
I can work around this by widening the definitions of decodeBar & decodeBaz to be of type State[Seq[Byte], Foo]. Is that the best way forward? Or can I take a different approach that avoids widening these types?
Functor.widen
Functor.widen should do the trick. Full compilable example (with kind-projector):
import cats.data.State
import cats.Functor
object FunctorWidenExample {
locally {
sealed trait A
case class B() extends A
val s: State[Unit, B] = State.pure(new B())
val t: State[Unit, A] = Functor[State[Unit, ?]].widen[B, A](s)
}
}
in your case, it would be something like:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBar)
case 1 => Functor[State[Seq[Byte], ?]].widen[Bar, Foo](decodeBaz)
}
Other possible work-arounds
(not really necessary, just to demonstrate the syntax that might be less known):
Explicit type ascriptions:
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(x => (x: Foo))
case 1 => decodeBaz.map(x => (x: Foo))
}
Using <:< as method (those things actually do have a meaningful apply):
val decodeFoo: State[Seq[Byte], Foo] = int.flatMap {
case 0 => decodeBar.map(implicitly: Bar <:< Foo)
case 1 => decodeBaz.map(implicitly: Baz <:< Foo)
}
I have a class like
case class A(a: Int, b: String)
and a function
def f(a: Int)(implicit b: String) =???
Is it possible to do something like this?
val a = A(11, "hello")
a match {
case A(a, implicit b) => f(a)
}
How can I make the parameter b implicit without explicitly declaring it after extraction.
I wouldn't worry about passing the argument implicitly, since you can easily provide it explicitly in this particular case:
case class A(a: Int, b: String)
def f(a: Int)(implicit b: String) =???
val a = A(11, "hello")
a match {
case A(a, b) => f(a)(b)
}
If you must pass the value implicitly, it needs to be declared in scope. For example:
a match {
case A(a, b) => {
implicit val s = b
f(a)
}
}
Also, as has been pointed out, don't use implicit with a common type. It's better if you wrap it in another class:
case class A(a: Int, b: String)
case class SomeCustomString(s: String)
def f(a: Int)(implicit b: SomeCustomString) =???
val a = A(11, "hello")
a match {
case A(a, b) => {
implicit val s = SomeCustomString(b)
f(a)
}
}
If you could explain the use case for the implicit argument, I could provide a better example.
Update: There is a kind of way to do what you want:
case class SomeCustomString(s: String)
case class A(a: Int, b: String) {
implicit val s = SomeCustomString(b)
}
def f(a: Int)(implicit s: SomeCustomString) =???
val a = A(11, "hello")
import a._
f(a.a)
Or, if you must have it within a pattern match, that last bit would be:
a match {
case x: A => {
import x._
f(x.a)
}
}
Update 2: Or, as yet another approach (again, with implicit largely redundant):
case class SomeCustomString(s: String)
case class A(a: Int, b: String) {
implicit val s = SomeCustomString(b)
def invokeF = f(a)
}
def f(a: Int)(implicit s: SomeCustomString) =???
val a = A(11, "hello")
a.invokeF
or
a match {
case x: A => x.invokeF
}
Does that help?
I tried to do something like this in Scala
trait A {
def foo(i: Int): Int
}
class B(val foo: Int => Int = _ + 1) extends A
But somewhat surprisingly I got this error from the compiler:
error: class B needs to be abstract, since method foo in trait A of type (i: Int)Int is not defined
class B(val foo: Int => Int = _ + 1) extends A
And this didn't work either with the same error:
class C extends A {
val foo: Int => Int = _ + 1
}
However this (obviously) worked:
class D(val bar: Int => Int = _ + 1) extends A {
def foo(i: Int): Int = bar(i)
}
scala> (new D).foo(5)
res1: Int = 6
So the question is, why is Scala treating def foo(i: Int): Int = ... differently than val foo: Int => Int = ...
Because, def foo(i: Int): Int defined in the trait: is a function which takes an integer as input and returns an integer as the output.
Where as, val foo: Int => Int = _ + 1 defined in the class: is a function, which returns a function. That's the key point, you have not implemented the one from trait. The foo in the class B is returning a function and the returned function takes an integer as input and provided an integer in the output.
Finally, I found an embarrassingly simple solution to my problem. All I had to do was to change my trait into this:
trait A { def foo: Int => Int }
And now it works as desired:
class B(val foo: Int => Int = _ + 1) extends A
scala> (new B).foo(6)
res3: Int = 7
> val foo: PartialFunction[String, Unit] = { case i: String => }
foo: PartialFunction[String,Unit] = <function1>
> val bar: PartialFunction[Int, Unit] = { case i: Int => }
bar: PartialFunction[Int,Unit] = <function1>
> foo orElse bar
PartialFunction[String with Int,Unit] = <function1>
What is String with Int?. I don't think that's even possible.
> (foo orElse bar)(new String with Int)
error: illegal inheritance from final class String
(foo orElse bar)(new String with Int)
^
error: class Int needs to be a trait to be mixed in
(foo orElse bar)(new String with Int)
^
Shouldn't it be PartialFunction[Nothing,Unit]?
What is String with Int?
It's an intersection type. I.e. a value of this type must be an Int and a String simultaneously.
I don't think that's even possible.
Yes, this is an uninhabited type. However, in general if you replace Int and String with some types A and B, you'll get PartialFunction[A with B, Unit] and the compiler doesn't have a special case for this.
Well, as said before, it's a compound type, and it works for any two types (classes, traits), as in the following code:
class B
class C extends B
class D extends C
val bf: PartialFunction[B, Unit] = {case b: B => println("some b") }
val cf: PartialFunction[C, Unit] = {case c: C => println("some c") }
val g = bf orElse cf
g(new D) // some b
It's just that sometimes it lacks of sense in some contexts. These links may prove useful:
http://www.scala-lang.org/old/node/110
http://www.scala-lang.org/files/archive/spec/2.11/03-types.html#compound-types
given following code:
abstract class MyTuple
...
case class MySeptet(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int, g: Int) extends MyTuple
case class MyOctet(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int, g: Int, h: Int) extends MyTuple
...
When using generated extractor, is it possible to skip remaining parameters, supposing they're unused ?
e.g. I don't want to write plenty of underscores in the following code snippet:
case MyOctet(a, b, _, _, _, _, _, _) => ... // uses only a and b
A simple approach to providing extractors for tupled classes that relies in fact in Array extractors, hence bypassing the original problem.
Let
abstract class MyTuple (order: Int)
case class MySeptet(coord: Array[Int]) extends MyTuple(7)
case class MyOctet(coord: Array[Int]) extends MyTuple(8)
and so for
val o = MyOctet( (1 to 8).toArray )
we can extract the first two elements in an octet like this,
o match {
case MyOctet( Array(a,b,_*) ) => a+b
case _ => 0
}
res: Int = 3
Note this does not address the problem of skipping remaining parameters in the case classes defined above.
Also note a weakness of this approach, illustrated as follows,
scala> val Array(a,b) = Array(1)
scala.MatchError: [I#16a75c0 (of class [I)
case o: MyOctet => o.a + o.b