I've supplied default values for the parameter "currentStack" in the "Empty" caseobject aswell as in the "Top" case class, but if I ommit the parameter when calling the push method I get the following message
"error: not enough arguments for method push: (newTop
: A, currentStack: Main.Stack[A])Main.Stack[A].
Unspecified value parameter currentStack.
currentBracket == '{') isBracketingValid(rest, bracketStack.push(currentBracket))".
I've tried constructing an empty stack aswell as an already filled one and called the push method on them it worked for adding one element. As soon as I tried to add another element with a further push call I get the above error message.
lazy val s1 = Empty
println(s1.push(1)) // <- works
//println(s1.push(1).push(2)) <- doesn't work
lazy val s2 = Top(3, Top(4, Empty))
println(s2.push(1)) // <- works
//println(s2.push(1).push(2)) <- doesn't work
here the Stack definition:
sealed trait Stack[+A] {
def push[A] (newTop: A, currentStack: Stack[A]): Stack[A] = ???
def pop: (Option[A], Stack[A]) = ???
}
case object Empty extends Stack[Nothing] {
override def push[A] (newTop: A, currentStack: Stack[A] = Empty): Stack[A] = Top(newTop, currentStack)
override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}
case class Top[A](val top: A, val rest: Stack[A]) extends Stack[A] {
override def push[A] (newTop: A, currentStack: Stack[A] = Top(top, rest)): Stack[A] = Top(newTop, currentStack)
override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}
def isBracketingValid(bracketString: String): Boolean = {
def isBracketingValid(bracketList: List[Char], bracketStack: Stack[Char]): Boolean = bracketList match {
case Nil => bracketStack == Empty
case currentBracket :: rest => {
lazy val previousBracket = bracketStack.pop._1.getOrElse('$')
lazy val isRestValid = isBracketingValid(rest, bracketStack.pop._2)
if (currentBracket == '(' ||
currentBracket == '[' ||
currentBracket == '{') isBracketingValid(rest, bracketStack.push(currentBracket))
else if (currentBracket == ')') previousBracket == '(' && isRestValid
else if (currentBracket == ']') previousBracket == '[' && isRestValid
else if (currentBracket == '}') previousBracket == '{' && isRestValid
else false
}
}
isBracketingValid(bracketString.toList, Empty)
}
EDIT
rewritten the Stack definition after Luis's hint to use "this", so no it doesn't cause the aformentioned problem as no stack is passed in, but I'm still interested in understanding the cause.
sealed trait Stack[+A] {
def push[A] (newTop: A): Top[A] = ???
def pop: (Option[A], Stack[A]) = ???
}
case object Empty extends Stack[Nothing] {
override def push[A] (newTop: A): Top[A] = Top(newTop, this)
override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}
case class Top[A](val top: A, val rest: Stack[A]) extends Stack[A] {
override def push[A] (newTop: A): Top[A] = Top(newTop, this.asInstanceOf[Stack[A]])
override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}
EDIT 2
refactored the stack defintion thanks to the insight of Luis to not use "asInstanceof" but accomplish the goal with a lower type bound instead. And also checked about why case clases should in general be final.
sealed trait Stack[+A] {
def push[B >: A] (newTop: B): Top[B] = ???
def pop: (Option[A], Stack[A]) = ???
}
case object Empty extends Stack[Nothing] {
override def push[A] (newTop: A): Top[A] = Top(newTop, this)
override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}
final case class Top[+A] (val top: A, val rest: Stack[A]) extends Stack[A] {
override def push[B >: A] (newTop: B): Top[B] = Top(newTop, this)
override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}
So, summarizing. The problem was that, even if in your two sub-classes, you had overridden the push
method to had a default value.
The method signature on the trait did not have such default. And, since that was the one you were calling, the compiler emitted the proper error.
You could had done pattern matching, to know which specific case of Stack
did you had, and that way the compiler would had found the signature with the default value. However, since the default was always a Stack
with the same shape as this, and since what you really needed was to just use this (since as an immutable collection, you can have structural sharing), it was better to just rewrite the method.
Following is the implementation of your Stack
that is more concise and simple (IMHO).
sealed trait Stack[+A] {
final def push[B >: A](newTop: B): Stack[B] =
Top(newTop, this)
final def pop: (Option[A], Stack[A]) = this match {
case Top(top, rest) => (Some(top), rest)
case Empty => (None, Empty)
}
}
final case class Top[+A](top: A, rest: Stack[A]) extends Stack[A]
case object Empty extends Stack[Nothing]