         }
     }
 
-    private func plusNumbers(arguments: [Expression], context: Context) throws -> Expression {
+    private func extractNumber(_ expression: Expression, context: Context) throws -> Number {
+        if case .numberValue(let number) = try expression.evaluate(context: context) {
+            return number
+        }
+        throw EvaluationError.notANumber(expression)
+    }
+
+    private func extractList(_ expression: Expression, context: Context) throws -> [Expression] {
+        if case .list(let list) = try expression.evaluate(context: context) {
+            return list
+        }
+        throw EvaluationError.notAList(expression)
+    }
+
+    private func extractString(_ expression: Expression, context: Context) throws -> String {
+        if case .stringValue(let string) = try expression.evaluate(context: context) {
+            return string
+        }
+        throw EvaluationError.notAString(expression)
+    }
+
+    private func numberOp(arguments: [Expression], context: Context, callback: (Number, Number) throws -> Expression) throws -> Expression {
         var numbers = arguments
         let left = try extractNumber(numbers.removeFirst(), context: context)
         let right = try extractNumber(numbers.removeFirst(), context: context)
-        return .numberValue(left.plus(right))
-    }
-
-    private func extractNumber(_ expression: Expression, context: Context) throws -> Number {
-        if case .numberValue(let number) = try expression.evaluate(context: context) {
-            return number
-        }
-        throw EvaluationError.notANumber(expression)
-    }
-
-    private func plusLists(arguments: [Expression], context: Context) throws -> Expression {
+        return try callback(left, right)
+    }
+
+    private func listOp(arguments: [Expression], context: Context, callback: ([Expression], [Expression]) throws -> Expression) throws -> Expression {
         var lists = arguments
         let left = try extractList(lists.removeFirst(), context: context)
         let right = try extractList(lists.removeFirst(), context: context)
-        return .list(left + right)
-    }
-
-    private func extractList(_ expression: Expression, context: Context) throws -> [Expression] {
-        if case .list(let list) = try expression.evaluate(context: context) {
-            return list
-        }
-        throw EvaluationError.notAList(expression)
-    }
-
-    private func plusStrings(arguments: [Expression], context: Context) throws -> Expression {
+        return try callback(left, right)
+    }
+
+    private func stringOp(arguments: [Expression], context: Context, callback: (String, String) -> Expression) throws -> Expression {
         var strings = arguments
         let left = try extractString(strings.removeFirst(), context: context)
         let right = try extractString(strings.removeFirst(), context: context)
-        return .stringValue(left + right)
-    }
-
-    private func extractString(_ expression: Expression, context: Context) throws -> String {
-        if case .stringValue(let string) = try expression.evaluate(context: context) {
-            return string
-        }
-        throw EvaluationError.notAString(expression)
+        return callback(left, right)
     }
 
     private func toNumbers(arguments: [Expression], context: Context) throws -> [Number] {

            let right = numbers.removeFirst()
            return .booleanValue(left.greaterThanOrEqualTo(right))
        case "eq":
            var numbers = try toNumbers(arguments: arguments, context: context)
            guard numbers.count == 2 else { throw EvaluationError.signatureMismatch }
            let left = numbers.removeFirst()
            let right = numbers.removeFirst()
            return .booleanValue(try left.equalTo(right))
        case "neq":
            var numbers = try toNumbers(arguments: arguments, context: context)
            guard numbers.count == 2 else { throw EvaluationError.signatureMismatch }
            let left = numbers.removeFirst()
            let right = numbers.removeFirst()
            return .booleanValue(try !left.equalTo(right))
        case "and":
            var bools = try toBools(arguments: arguments, context: context)
            guard bools.count == 2 else { throw EvaluationError.signatureMismatch }

            guard arguments.count == 2 else { throw EvaluationError.signatureMismatch }
            let result: Expression
            do {
                result = try numberOp(arguments: arguments, context: context) {
                     .numberValue($0.plus($1))
                }
            } catch EvaluationError.notANumber {
                do {
                    result = try listOp(arguments: arguments, context: context) {
                        .list($0 + $1)
                    }
                } catch EvaluationError.notAList {
                    result = try stringOp(arguments: arguments, context: context) {
                        .stringValue($0 + $1)
                    }
                }
            }
            return result
