Lambda Injection (Higher order functions)

Hi, I understand from other posts I’ve seen here that FQL is not lazily evaluated, and so injection is, for all intents and purposes, impossible.

I would like to see some way to directly override that in FQL, such as an EvalLambda or some such, that we could use to pass internal values to a Lambda passed in as an argument.

My use case in particular is I would like to have some set of “query builder” UDFs that I can then create other, more restricted, UDFs out of. Those more restricted UDFs I can give permission to users to use, whereas those UDFs accepting Lambdas would not be available to them.

Query(
  Lambda(
    ["match_set", "filter_lambda"],
    Map(
      Paginate(
        Filter(
          Var("match_set"),
          Var("filter_lambda")
        )
      ),
      Lambda(
        "refToFetch",
        Get(Var("refToFetch")) 
      )
    )
  )
)

In my example above I would like to be able to pass in a filter_lambda and have it evaluated as if it were directly written inside the Filter.

With EvalLambda a possible API could be:

EvalLambda(lambda, args)

thus making my above request possible with something like

Query(
  Lambda(
    ["match_set", "filter_lambda"],
    Map(
      Paginate(
        Filter(
          Var("match_set"),
          Lambda(
            "ref",
            EvalLambda(Var("filter_lambda"), Var("ref"))
          )
        )
      ),
      Lambda(
        "refToFetch",
        Get(Var("refToFetch")) 
      )
    )
  )
)

It’d be even cooler if it could draw in the "ref" implicitly from the surrounding Filter.

Apologies if I’m missing something obvious here

Higher order UDFs would indeed be awesome! You’ve shared an excellent use case – passing lambdas to Map, Reduce, and Filter functions.

Workaround with pre-defined UDFs

A workaround for passing eval-able lambdas into other functions is to use pre-define UDFs instead. This restricts users from selecting from a limited set of functions, rather the full breadth of FQL. But if you need the latter, you might consider letting users have access to resources to make their own queries anyway (As opposed to only exposing a few UDFs).

UDFs are referenced by their name, so instead of a filter_lambda parameter you can have a filter_udf_nameparameter, and use it like this:

        // ...
        Filter(
          Var("match_set"),
          Lambda("values", Call(Var("filter_udf_name"), Var("values")))
        )
        // ...

other use cases

It’s come up some other times that I am aware of in the forums. Higher order functions could help solve some other known problems like reusing Role predicates, or creating new functions that close over provided values.

One workaround for defining new UDFs is to store configurable values in the data of the new UDF, rather than provide a lambda.

Wow, thanks for the speedy reply!
Since I’m using this for a personal project at the minute, my “workaround” was to go outside of Fauna and build the queries in my nodejs API layer. I can certainly look into the more restricted calling UDF route, but my ideal would be to be able to build and combine filters and matches on multiple fields at once, potentially even across tables, as part of a user data display, so writing custom UDFs for each case would get tricky!

As you say it’d be awesome to have higher order UDFs so I could have all of this built at the database level and so could keep my API and client separate :slight_smile: