class StringLeftRightFunction extends Scalar<String, Object> {

    public static void register(Functions.Builder module) {
        module.add(
            Signature.scalar(
                "left",
                DataTypes.STRING.getTypeSignature(),
                DataTypes.INTEGER.getTypeSignature(),
                DataTypes.STRING.getTypeSignature()
            ),
            (signature, boundSignature) ->
                new StringLeftRightFunction(signature, boundSignature, StringLeftRightFunction::left)
        );
        module.add(
            Signature.scalar(
                "right",
                DataTypes.STRING.getTypeSignature(),
                DataTypes.INTEGER.getTypeSignature(),
                DataTypes.STRING.getTypeSignature()
            ),
            (signature, boundSignature) ->
                new StringLeftRightFunction(signature, boundSignature, StringLeftRightFunction::right)
        );
    }

    private final BiFunction<String, Integer, String> func;

    private StringLeftRightFunction(Signature signature,
                                    BoundSignature boundSignature,
                                    BiFunction<String, Integer, String> func) {
        super(signature, boundSignature);
        this.func = func;
    }

    @Override
    public String evaluate(TransactionContext txnCtx, NodeContext nodeCtx, Input[] args) {
        assert args.length == 2 : String.format(Locale.ENGLISH,
                                                "number of arguments must be 2, got %d instead",
                                                args.length);
        String str = (String) args[0].value();
        Number len = (Number) args[1].value();
        if (str == null || len == null) {
            return null;
        }
        return len.intValue() == 0 || str.isEmpty() ? "" : func.apply(str, len.intValue());
    }

    private static String left(String str, int len) {
        if (len > 0) {
            return str.substring(0, Math.min(len, str.length()));
        }
        final int finalLen = str.length() + len;
        return finalLen > 0 ? str.substring(0, finalLen) : "";
    }

    private static String right(String str, int len) {<FILL_FUNCTION_BODY>}
}

        if (len < 0) {
            return str.substring(Math.min(-len, str.length()));
        }
        final int finalLen = str.length() - len;
        return finalLen <= 0 ? str : str.substring(finalLen);
    
<methods>public BoundSignature boundSignature() ,public Scalar<java.lang.String,java.lang.Object> compile(List<io.crate.expression.symbol.Symbol>, java.lang.String, io.crate.role.Roles) ,public transient abstract java.lang.String evaluate(io.crate.metadata.TransactionContext, io.crate.metadata.NodeContext, Input<java.lang.Object>[]) ,public io.crate.expression.symbol.Symbol normalizeSymbol(io.crate.expression.symbol.Function, io.crate.metadata.TransactionContext, io.crate.metadata.NodeContext) ,public io.crate.metadata.functions.Signature signature() <variables>public static final Set<io.crate.metadata.Scalar.Feature> DETERMINISTIC_AND_COMPARISON_REPLACEMENT,public static final Set<io.crate.metadata.Scalar.Feature> DETERMINISTIC_ONLY,public static final Set<io.crate.metadata.Scalar.Feature> NO_FEATURES,protected final non-sealed BoundSignature boundSignature,protected final non-sealed io.crate.metadata.functions.Signature signature