Lanak
Lanak

Reputation: 23

Scala: generics and implicit

I have to develop class StackMachine[T]. If T = Boolean, then there should be logical operations. If T = Int,Double,Long and etc. there should be ariphmetic operations. Firstly i developed class Stack[T].

class Stack[T](val stack: List[T]) {
    val length: Int = stack.length
    def isEmpty: Boolean = {length == 0}
    def push(x: T): Stack[T] = {
      new Stack[T](x :: stack)
    }

     def peak: T = {
       if (this.isEmpty)
         throw new ArrayIndexOutOfBoundsException
         else stack.head
     }
    def pop(): Stack[T] = {
      if (this.isEmpty)
        throw new ArrayStoreException()
      val x :: xs = stack
      new Stack[T](xs)
    }

The thin is that i dont know how to develop StackMachine[T] the presence of operations in which depends on the type. I tried this:

case class StackMachine[T](val stack:Stack[T]){
    def const(x: T): StackMachine[T] = {new StackMachine[T](new Stack[T](this.stack.push(x).stack))}
    def dup: StackMachine[T] = {new StackMachine[T](new Stack[T](this.stack.push(this.stack.peak).stack))}
    def swap: StackMachine[T] = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.peak
      val finalStack = secondStack.pop().push(startPeak)
      StackMachine[T](stack)
    }
    def and(): StackMachine[Boolean] = {
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak && secondPeak).stack))

    }

    def or: StackMachine[Boolean] = {
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.pop().peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak || secondPeak).stack))
    }

    def xor: StackMachine[Boolean] = {
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.pop().peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak ^ secondPeak).stack))
    }

    def sum(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.plus(startPeak,input)).stack))
    }

    def dif(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.minus(startPeak,input)).stack))
    }

    def mul(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak).*(N.toDouble(input)).asInstanceOf[T]).stack))
    }

    def div(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak)./(N.toDouble(input)).asInstanceOf[T]).stack))
    }

    def min(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.min(startPeak,input)).stack))
    }
    def max(input : T)(implicit N: Numeric[T])  = {
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.max(startPeak,input)).stack))
    }



  }

But this is wrong, because operations shouldnt have input parameters because all variables have to be taken from Stack. More than that, this way i cant create diff and mul functions. I thoght to make StackMachine[T] abstract and use imlplicit object, but failed because in that case, my functions cant return StackMachine. May be i just dont understand implicit well enough or there is another way of doing this?

Upvotes: 2

Views: 96

Answers (1)

Then yeah it seems the project is intended to be solved using a typeclass.

For example, see this small one for Boolean-like and:

sealed trait BehavesAsBoolean[T] {
  def and(t1: T, t2: T): T
}

object BehavesAsBoolean {
  implicit final val BooleanBehavesAsBoolean: BehavesAsBoolean[Boolean] =
    new BehavesAsBoolean[Boolean] {
      override def and(b1: Boolean, b2: Boolean): Boolean =
        b1 && b2
    }
}

final class StackMachine[T](stack: Stack[T]) {
  def and(implicit ev: BehavesAsBoolean[T]): Option[StackMachine[T]] =
    for {
      // I changed the implementation of pop to return an Option[(T, Stack[T])]
      (b1, s2) <- stack.pop
      (b2, s3) <- s2.pop
    } yield {
      new StackMachine(s3.push(ev.and(b1, b2)))
    }
}

Of course, you may still prefer to throw exceptions rather than using Option
Anyways, I hope this helps you to finish the code.


You can see the code running here.

Upvotes: 5

Related Questions