New Feature: Static functional binding to BIFs

Functional programming and higher order functions (functions that receive or return other functions) have given us a lot of really ways to transform data on the fly with more concise syntax. If I have an array of string and I want them all uppercased, I can write this:

[ "luis", "brad", "jon" ].map( name -> uCase( name ) )

Note, in the example above

  • Omitting the parentheseis around our lambda argument name is supported by ACF, but not by Lucee. BoxLang, of course, allows this!
  • We are also using the lighter-weight lambda syntax which uses a “skinny arrow” (->). This has no scope binding like closures and performs faster)

Of course, we don’t have to declare a closure or lambda inline, we can reference another unary function object like so. (Unary means accepting a single argument)

function makeUpperCase( String arg ) {
  return uCase( arg )
}
[ "luis", "brad", "jon" ].map( makeUpperCase )

This, of course, requires even more boilerplate than the first option! Have you ever wished you could just pass in the ucase BIF (Built In Function) directly to use? Introducing static functional binding to BIFs!

There is already a precedent for binding to static methods on classes (whether Box Classes, or Java classes) of this:

ClassName::methodName

There is a variation of this used in several languages for statically referencing a method from a global scope where we just omit the class name entirely:

::methodName

That is an expression which yields an invocable BoxLang function that can be executed just like our makeUpperCase() example UDF above. Let’s see what our final form looks like:

[ "luis", "brad", "jon" ].map( ::uCase ) // [ "BRAD","LUIS","JON" ]

Nice and sweet! This will work for any BIF accepting a single argument for map(), or each() functional constructs.

[1.2, 2.3, 3.4].map( ::ceiling );    // [ 2, 3, 4 ]

["brad","luis","jon"].map( ::hash ); // [ "884354eb56db3323cbce63a5e177ecac", "502ff82f7f1f8218dd41201fe4353687", "006cb570acdab0e0bfc8e3dcb7bb4edf" ]

You can even use BIFs that accept two args with a higher order function that accepts a Bi Consumer .

myQry = queryNew( "name,position", "varchar,varchar", [ ["Luis","CEO"], ["Jon","Architect"], ["Brad","Chaos Monkey"] ]);

myQry.reduce( ::arrayAppend, [] ) // Array of structs for each row...

This example reduces a query object down to an array of structs in a very small amount of code.

Hopefully this unlocks some new productivity for you. This feature is available right now on the bleeding edge, or this Friday in the next beta release and applies to Boxlang source files.

2 Likes