For anyone subscribed, here is another new feature we’ve added along the same lines that I’ve personally wanted for years!
And finally this enhancement as well has come from this discussion:
@seancorfield This one is based on what we discussed as
SomeClass::.instanceMethod – treating such unbound Java instance method reference
We analyzed the syntax as well as how some other dynamic languages and even JS frameworks handle this sort of thing. In the end
- Since “member methods” in CF/BL are more of a loose concept, and can be member methods on a java class, or “magic” member methods from CF/BL types like
numeric
orarray
, the class name didn’t really work in all scenarios, so we simplified the syntax to simply.methodName
- instead of
::.
we went with just a period in front of the construct to look like a headless method call which is a bit more readable. - I’m not sure about Clojure, but java’s
String::concat
method binding doesn’t allow args to be specified. We are also allowing.methodName( arg )
to include arg(s) along with the member method binding.
pendingOrders.each( .submit )
or
[ "brad", "luis", "jon" ].map( .left( 1 ) ); // [ "b", "l", "j" ]
There are more examples in the linked thread, and even more examples on the ticket.
@bdw429s I would love to see a new high-order function that worked like this BoxLang - Issues - Welcome
Having groupBy
built-in would be nice although it’s a fairly simple function to implement via reduce
– see my comments on that issue.
Yes. My problem with reduce is that it can’t be paralleled so it’s not efficient for big lists.
“group by” inherently involves a non-parallelizable operation: collecting all the results into a pair of lists.
If your predicate is expensive, you can parallel map it over the list. But you still have to walk the whole list to perform the partition based on the result of the predicate. You can’t avoid that.
The more simplified “group by” based on filter() + side effects is parallelizable, as shown in my example. Limiting the grouping to a simple pass/fail is less flexible but can’t get much more efficient, though maintaining the original order is out the window.
As long as .append()
is completely thread-safe, yes, you could parallelize the side-effecting filter()
call – but you’d likely have a lot of contention on the .append()
with a lot of threads trying to call it at the same time, so you’d probably get blocking behavior and the whole thing would end up being as slow as sequential thread-safe .append()
calls would allow.
And, again, if your predicate is cheap (which it has been in all the examples so far), the overhead of parallelization and contention on the thread-safe accumulating resource would completely blow out any benefit of actually parallelizing it in the first place.
Even if you went with virtual threads in JDK 21, you’d still have some thread creation overhead here and you wouldn’t be able to avoid the contention on .append()
(and, hopefully, none of that code uses synchronized
to gain thread-safety otherwise you’re going to be pinning O/S-level threads and you’re even worse off than the non-virtual case).
Parallelization isn’t a magic “get out of jail” performance card: it has trade-offs!
That’s true. I have to tune parallel operations often and sometimes scrap them entirely. Parallels inside of other parallels (pulling large lists of benchmark constituents from an API endpoint with multiple threads, then mapping results in parallel inside the thread to normalize the data, for example) don’t always work well. In most cases, I limit to 4-6 threads.