Scala traits for arithmetic expressions
In terms of you where to put the eval function, both are fine really. I have seen bits of both in libraries likes cats or scalaz. And given how easily you get one from the other, it does not make a massive difference architecturally. 

However, I see a few issues on the side of validating your expressions, that I am honestly acking to improve :)

1) Both sides of an expression can result in an error, but you will show only one of them to the user.

2) These nested match-case expressions can be massively simplified by using one of the many functional validation libraries. 

Let me attempt to solve both of these by using Scalactic's validation:

import org.scalactic._
import Accumulation._

object Arithmetics extends App {

  sealed trait Expression {
    def eval: Double Or Every[String] = this match {
      case Addition(l, r) =&gt; withGood(l.eval, r.eval)(_ + _)
      case Subtraction(l, r) =&gt; withGood(l.eval, r.eval)(_ - _)
      case Division(l, r) =&gt;
        def isNonZero(i: Double) = if (i != 0) Pass else Fail(s"divisor evaluated to zero: $r")
        withGood(l.eval, r.eval.when(isNonZero))(_ / _)
      case SquareRoot(v) =&gt;
        def isGreaterThanZero(i: Double) = if (i &gt;= 0) Pass else Fail(s"Expression under square root evaluated to $i (&lt;0): $v")
        v.eval.when(isGreaterThanZero).map(Math.sqrt)
      case Number(v) =&gt; Good(v)
    }
  }
  final case class Addition(left: Expression, right: Expression) extends Expression
  final case class Subtraction(left: Expression, right: Expression) extends Expression
  final case class Division(left: Expression, right: Expression) extends Expression
  final case class SquareRoot(value: Expression) extends Expression
  final case class Number(value: Int) extends Expression

  val failing = Addition(SquareRoot(Number(-5)), Division(Number(1), Subtraction(Number(1), Number(1))))
  println(failing.eval)
  // Bad(Many(Expression under square root evaluated to -5.0 (&lt;0): Number(-5), divisor evaluated to zero: Subtraction(Number(1),Number(1))))

  val valid = Addition(SquareRoot(Number(25)), Division(Number(1), Number(2)))
  println(valid.eval)
  //Good(5.5)
}


You can see that you can get multiple errors at once, and much fewer lines of code, plus a human readable syntax.

In case you want to stick with your custom Calculation, it could look like this:

object Arithmetics2 extends App {

  sealed trait Calculation[A] {
    def map[B](f: A =&gt; B): Calculation[B] = this match {
      case Success(value) =&gt; Success(f(value))
      case Failure(errors) =&gt; Failure(errors)
    }
    def zip[B](other: Calculation[B]): Calculation[(A, B)] = (this, other) match {
      case (Success(valueA), Success(valueB)) =&gt; Success((valueA,valueB))
      case (Failure(errorsA), Failure(errorsB)) =&gt; Failure(errorsA ++ errorsB)
      case (_, Failure(errors)) =&gt; Failure(errors)
      case (Failure(errors), _) =&gt; Failure(errors)
    }
    def filter(f: A =&gt; Boolean, msg: A =&gt; String): Calculation[A] = this match {
      case Success(value) if f(value) =&gt; Success(value)
      case Success(value) =&gt; Failure(List(msg(value)))
      case Failure(errors) =&gt; Failure(errors)
    }
  }
  case class Success[A](value: A) extends Calculation[A]
  case class Failure[A](errors: List[String]) extends Calculation[A]


  sealed trait Expression {
    def eval: Calculation[Double] = this match {
      case Addition(l, r) =&gt; l.eval.zip(r.eval).map{ case (a,b) =&gt; a + b}
      case Subtraction(l, r) =&gt; l.eval.zip(r.eval).map{ case (a,b) =&gt; a - b}
      case Division(l, r) =&gt;
        l.eval.zip(r.eval.filter(_ != 0, _ =&gt; s"divisor evaluated to zero: $r")).map{ case (a,b) =&gt; a / b}
      case SquareRoot(v) =&gt;
        v.eval.filter(_ &gt;= 0, i =&gt; s"Expression under square root evaluated to $i (&lt;0): $v").map(Math.sqrt)
      case Number(v) =&gt; Success(v)
    }
  }
  final case class Addition(left: Expression, right: Expression) extends Expression
  final case class Subtraction(left: Expression, right: Expression) extends Expression
  final case class Division(left: Expression, right: Expression) extends Expression
  final case class SquareRoot(value: Expression) extends Expression
  final case class Number(value: Int) extends Expression

  val failing = Addition(SquareRoot(Number(-5)), Division(Number(1), Subtraction(Number(1), Number(1))))
  println(failing.eval)
  // Failure(List(Expression under square root evaluated to -5.0 (&lt;0): Number(-5), divisor evaluated to zero: Subtraction(Number(1),Number(1))))

  val valid = Addition(SquareRoot(Number(25)), Division(Number(1), Number(2)))
  println(valid.eval)
  //Success(5.5)
}


Notice that it actually is not that lengthy/hard to make some decent validation mechanism.
Once you get a bit more into functional programming, you will see that functions like map, filter or zip can be found in many places. You will find them for example on most collections, like List.