Does Scala have a way to override methods from that are called from different class objects?

Hi all, I’ve hit a wall while trying to implement a (somewhat) trivial problem:

I can only modify the RunDataStructure class, and can’t implement a different pop() method on LIFO() class itself.

    abstract class DataStructures

    case class FIFO() extends DataStructures
    case class LIFO() extends DataStructures

    class SelectDataStructure(val structure: DataStructures){}

    class RunDataStructure[A](val sds: SelectDataStructure){
    var elements: List[A] = Nil

    //basic FIFO implementation - push() method is the same for LIFO too
    def push(element: A): Boolean = {
    elements = element :: elements
    true
    }

    //need to override pop() so it's different for LIFO()
    def pop(): Option[A] = {
    if (elements == Nil) {
      return None
    }
    val head = elements.reverse.head
        elements = elements.reverse.tail.reverse
        Some(head)
      }
    }

I have to run and pass these test cases (which I shouldn’t modify either)

"A priority collection" should "pop the smallest element first" in {
      val sds= new SelectDataStructure(FIFO())
      val fifo= new RunDataStructure[Int](sds)
      fifo.push(1)
      fifo.push(2)
      fifo.push(3)
      assert(fifo.pop() === Some(1))
      assert(fifo.pop() === Some(2))
      assert(fifo.pop() === Some(3))
    }

"A LIFO collection" should "pop elements that were inserted first after elements that were inserted later" in {
      val sds= new SelectDataStructure(LIFO())
      val lifo= new RunDataStructure[Int](sds)
      lifo.push(1)
      lifo.push(2)
      lifo.push(3)
      assert(lifo.pop() === Some(3))
      assert(lifo.pop() === Some(2))
      assert(lifo.pop() === Some(1))
    }

Is there a way to override pop() functions from the RunDataStructure class so they differ when they’re called on the tests?

The setup and requirements seem somewhat odd and non-idiomatic, and it might help to know the exact exercise text. From my understanding, given your description, one could pattern match over structure inside pop() in order to supply polymorphic behavior, for example.

2 Likes

Thanks for your reply @sangamon , and sorry for my late one.

Yeah, tell me about it…

The exact exercise text is this:

Implement the configurable collection using runtime parameters in the file runtime/Collection.scala. We provide the collection interface and a configuration class that gets passed to the constructor of the collection class. Your implementation must change its behavior based on the values in the configuration object.

I knew about the pattern match by type, but the thing is, the match variable should be a parameter of method pop() leading to pop(match: SelectDataStructure), and it is stated that we can’t modify the tests.

I also tried a naive if else block: if(config == LIFO) after FIFO implementation, which doesn’t even pass the 1st test. When I add only FIFO code the 1st test is passed.

I tried to read RunDataStructure as a string and compare it with “LIFO”, but its value is the memory location of the instantiated object.

It is a fairly simple exercise but I can’t think of anything I know of to make it work.

It’s already accessible from within pop() via sds.structure.

2 Likes

Oh My God, You are absolutely right, this worked!

I didn’t even think of that it was right before my eyes the whole time, my code is so convoluted I didn’t see this!

Thanks a lot @sangamon!

Btw, Is it okay for you if you we connect on some social platform of your liking? I
feel that I might need a little push later.

This social platform here is just fine. :slight_smile:

EDIT/Addendum: If you prefer more real-time conversation about Scala related questions, there’s gitter and Discord, see details here. (I only lurk there occasionally, I prefer async communication.)

1 Like

I need your help once more @sangamon

So I simplified the exercise code as I have to pass (a lot) more than 2 tests. Using the hint you gave me I was able to pass 3 tests as they were somehwhat similar.

I’ve hit a speedbump again.

Recall the code:

class SelectDataStructure(val structure: DataStructures){}

In reality it has another parameter:

class SelectDataStructure(val structure: DataStructures, val size: Option[Int] = None){}

I modified that param to be val size: Option[Int] = Some(3) as I had to push and pop 3 values on tests.

The 4th test goes like this:

"A collection with capacity" must "reject new elements when it is full" in {
    val sds = new SelectDataStructure(FIFO(), size = Some(2))
    val fifoWithSize = new RunDataStructure[Int](sds)
    assert(fifoWithSize.push(1))
    assert(fifoWithSize.push(2))
    assert(fifoWithSize.push(2) === false)
    assert(fifoWithSize.size === 2)
  }

The problem here is that if my size is always Some(3), therefore assertion 2, 3 and 4 will always fail.

I added the following code to (sort of) make test 4 work:

  def ssize: Int = {
    elements.size
  }

sds.size match{
case c =>
        if (ssize >= sds.size.get) {
           elements diff elements
           //on test 4: Let's see what elements are here: List(2, 1)
           println("Let's see what elements are here: " + elements)

          //on test 4: Size is: 2
          println("Size is: " + sds.size.get)
          false
        }
        else true
    }
  }

The main question is: how to make the parameter val size: Option[Int] = Some(2) in the if block? (Of course) I get an error that val is immutable whenever I try to reassign it. Now this is something I can’t think my way out of.

I’m in dire need of your help.

Others reading along may want to chime in, too - but might be less inclined to do so if you make this look like a dialogue.

Don’t ever use Option#get - this will crash at runtime when applied to None. (IMHO this really shouldn’t be part of the API at all, or at least be called unsafeGet.)

Your pattern match on sds.size doesn’t accomplish anything other than giving you the alias name c (and you’re not even using this). Check the pattern matching doc page I linked to before and match to the two possible cases for Option.

For now, I’d suggest to split the calculation of the Boolean (can the element be added? - the return value) and the actual modification, e.g.

val canPush: Boolean =
  sds.size match {
    // - match over the two possible cases for Option
    // - if a size limit is provided, compare to elements.size
    // - otherwise return the appropriate boolean value for the unlimited case 
  }
if(canPush)
  elements = element :: elements
canPush

Alternatively, perhaps more concise, you could look for a method or a combination/chain of methods on Option that can be used to calculate the canPush.

2 Likes

val size: Option[Int] = None here just means that the default value of size is None (I guess None in this context means that there is no size limit, but that should probably be specified in the tests somewhere). If you call new SelectDataStructure(FIFO()) that default value will be used, but if you call new SelectDataStructure(FIFO(), size = Some(2)) then Some(2) will be used instead. So IIUC the problem you describe here does not exist.

1 Like

TL;DR 4th assertion fails, even though size should be 2, it’s actually 3

Hello @sangamon & @Jasper-M, sorry for taking so long to reply as I didn’t have my laptop until later today.

Before I reply to your suggestion sangamon, I’ll show you what I did so far and my understanding of why the tests don’t pass:

I’ve modified the if block so that I don’t use .get method anymore, it was all over the internet that I’ll get NoSuchElementExceptions. Instead, I created a method to convert Int to Option[Int] for method ssize

def toOption(i: Int): Option[Int] = {
    try{
      Some(i)
    }catch{
      case e: Exception => None
    }
  }

With this I can compare easily:

sds.size match{
case c: Option[Int] =>
if (toOption(ssize) > sds.size) {

//ListToInt casts List[A] to List[Int],
//filtering predicate returns an empty list,
//as all the assertions push 1 and 2
println(ListToInt(elements).filter(_ > 2))

//Returns 3 (to my surprise), 
//even though size is instantiated as 2 on the test
println(elements.size)
  false
  }
   else true
}

I pass 3 assertions now, but 4th one assert(fifoWithCapacity.size === 2) always fails, as size is 3 unfortunately.

I tried to reassign size to 2 but it was immutable. I can’t call the pop() method. I’m stuck again…

Going to your suggestion: it’s true that I’m not using case c, because I can’t think of any use of it. Did the same with cases in pop() and they work just fine. The patten matching doc is calling a function case p: Phone => p.screenOff but I don’t know how to apply it to my scenario.

Going to your suggestion: I love how you suggested the canPush boolean, I’d never think of elegant code like this in a thousand years. But I’m afraid to say I don’t understand how to implement the comments in your pattern match body. I’m sorry.

@Jasper-M what you’re saying makes sense and this was my initial interpretation as well, but after running the test I was stunned.

Here’s what happens when I run it:

I’m pretty new to Scala and I’m not a stellar coder either, and I think there’s some reason I don’t know of that makes the program behave like this.

Hi.

It is really hard to follow you along, and it seems you misunderstood what @sangamon was trying to convey.

This is completely weird. It is simply equivalent to Some(i). (and you should not try to catch any Exception)

This is not really better. sds.size is an Option[Int]. There is no use in trying to match whether it is an Option[Int]. What sangamon was hinting at, is that an Option is a union type, ie. it has only two possible “representations”: either it is None, or it is Some(value).

These are the cases that you should try to match against.

val canPush = sds.size match {
  case None => true // always
  case Some(limit) => ... // is `elements` already full?
}

Look at your condition again: toOption(ssize) > sds.size – only when your internal collection is bigger than the limit, you return false. Otherwise you accept the element and return true… :bulb:

1 Like

Hi @cbley cbley, thanks for the reply.

The thing is, when I do toOption(ssize) >= sds.size, assertion 2 fails, as when size it’s equal to 2 it returns false :frowning_face:

The good news is that the List size is only 2 here, but I can’t get to the other tests.

Just tried case None => true case Some(2) => true, but then assertion 3 fails.

What do you suggest?

I’d suggest visiting scastie to post runnable snippets.

There is also a homework-help on the typelevel discord, and it looks like they offer help for more (or less) than typelevel homework. Chat might be easier.

It looks like you might have a few questions about Scala syntax and usage, and also about the homework.

Your condition is still flawed, as you are effectively checking “did my collection already overflow?”. That is; after the fact when it is too late.

Since this is inside the push operation, what you should check is: “is there still room for another element?” / “can I push the element that was just given as an argument?”

Of course, this is not correct. You can only simply return true in the None case, since then your collection is unbounded.

val canPush = sds.size match {
  case None => true // always
  case Some(limit) => ... // is `elements` already full?
}

You should only fill in the blanks. You should insert an expression which returns true if your collection is not already full, and false otherwise. (hint: just use limit and element.size variables)