public FunctionEvaluator evaluatorOf(String modelName, String ... names) {
        return requireModel(modelName).evaluatorOf(names);
    }
@Test
    public void testBindingValidation() {
        List<ExpressionFunction> functions = new ArrayList<>();
        ExpressionFunction function = new ExpressionFunction("test", RankingExpression.from("sum(arg1 * arg2)"));
        function = function.withArgument("arg1", TensorType.fromSpec("tensor(d0[1])"));
        function = function.withArgument("arg2", TensorType.fromSpec("tensor(d1{})"));
        functions.add(function);
        Model model = new Model("test-model", functions);

        try { // No bindings
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.evaluate();
        }
        catch (IllegalStateException e) {
            assertEquals("Argument 'arg1' must be bound to a value of type tensor(d0[1])",
                         Exceptions.toMessageString(e));
        }

        try { // Just one binding
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.bind("arg2", Tensor.from(TensorType.fromSpec("tensor(d1{})"), "{{d1:foo}:0.1}"));
            evaluator.evaluate();
        }
        catch (IllegalStateException e) {
            assertEquals("Argument 'arg1' must be bound to a value of type tensor(d0[1])",
                         Exceptions.toMessageString(e));
        }
        try { // Just the other binding
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.bind("arg1", Tensor.from(TensorType.fromSpec("tensor(d0[1])"), "{{d0:0}:0.1}"));
            evaluator.evaluate();
        }
        catch (IllegalStateException e) {
            assertEquals("Argument 'arg2' must be bound to a value of type tensor(d1{})",
                    Exceptions.toMessageString(e));
        }

        try { // Wrong binding argument
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.bind("argNone", Tensor.from(TensorType.fromSpec("tensor(d1{})"), "{{d1:foo}:0.1}"));
            evaluator.evaluate();
        }
        catch (IllegalArgumentException e) {
            assertEquals("'argNone' is not a valid argument in function 'test'. Expected arguments: arg1: tensor(d0[1]), arg2: tensor(d1{})",
                         Exceptions.toMessageString(e));
        }

        try { // Wrong binding type
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.bind("arg1", Tensor.from(TensorType.fromSpec("tensor(d3{})"), "{{d3:foo}:0.1}"));
            evaluator.evaluate();
        }
        catch (IllegalArgumentException e) {
            assertEquals("'arg1' must be of type tensor(d0[1]), not tensor(d3{})",
                         Exceptions.toMessageString(e));
        }

        try { // Attempt to reuse evaluator
            FunctionEvaluator evaluator = model.evaluatorOf("test");
            evaluator.bind("arg1", Tensor.from(TensorType.fromSpec("tensor(d0[1])"), "{{d0:0}:0.1}"));
            evaluator.bind("arg2", Tensor.from(TensorType.fromSpec("tensor(d1{})"), "{{d1:foo}:0.1}"));
            evaluator.evaluate();
            evaluator.bind("arg1", Tensor.from(TensorType.fromSpec("tensor(d0[1])"), "{{d0:0}:0.1}"));
        }
        catch (IllegalStateException e) {
            assertEquals("Cannot bind a new value in a used evaluator",
                         Exceptions.toMessageString(e));
        }

    }