Skip to content

Function

Santanu Sinha edited this page Feb 3, 2022 · 7 revisions

Hope provides supports for functions to be used to generate values, partial values or parameters of other functions in both LHS or RHS of a Hope expression. A function may have zero or more parameters. Parameters can be hard coded values, functions, json pointer or json path that get evaluated during runtime.

    final ObjectMapper mapper = new ObjectMapper();
    final JsonNode node = mapper.readTree("{ \"a\" : 2, \"b\" : 3, \"c\" : 5 }");
    Assert.assertTrue(
        HopeLangEngine.builder()
            .build()
            .evaluate(""math.negate(math.sub(math.add(\"$.a\", \"$.b\", 7), 13)) == 1"", node));

A function call is always evaluated during evaluation phase even if all parameters are resolved even during parse time. This means that all type checking etc is done done only during evaluation phase and never during parse phase.

Custom functions

Clients might need to provide custom functions for use in Hope expressions.

The following details will come in handy when trying to implement a function:

  • The function class need to derive from HopeFunction class
  • All implementations need to provide a name for the function that can be used in an expression. This is acheived by annotating the implementation with @FunctionImplmentation(name). Functions without this annotation cannot be registered to Hope.
  • A function can accept zero or more parameters of Value type. What this means that core data types, arrays, json-pointers, json-paths and function calls can be passed as parameters to a function.
  • Implementors can use static functions in Converter class to get evaluated values of parameters. Never assume that a value will not be evaluated at runtime.
  • A function must return a subclass of Value as response.
  • Variable arguments are supported. This can be used to implement functions like math.sum(...) or math.prod(...). If a variable type param is used, the function implementation class must have only one argument of variable type. Check implementation of math.sum() in Sum.java to see example.
  • *Overloading of functions is achieved by having multiple constructors with differing number of arguments. See implementation of str.substr in SubStr.java to see this in action.
  • Hope will instantiate a copy of the function per eval, it is advisable to not use mutable statics members in the implementation code to keep everything thread safe

Sample custom function

@FunctionImplementation("ss.blah")
public class Blah extends HopeFunction<StringValue> {
    @Override
    public StringValue apply(Evaluator.EvaluationContext evaluationContext) {
        return new StringValue("blah");
    }
}

public class CustomTest {
    @Test
    public void testBlah() {
        final HopeLangEngine hope
            = HopeLangEngine.builder()
                .registerFunction(Blah.class) //Register class by class
                .addPackage("org.ss.hope.customfunctions") //Register a package full of functions
            .build();
        
        JsonNode node = ...
        Assert.assertTrue(hope.evaluate("ss.blah() == \"blah\"", node));
    }
}
Clone this wiki locally