LuceeLang: What Kind Of Language Should It Be?

This is an adjunct to my “How Compatible With CFML Should It Be?” post – http://lang.lucee.org/t/luceelang-how-compatible-with-cfml-should-it-be/228 – and in this one I want to look more at the nature of what we want LuceeLang to be. I think this is much less contentious but I think it’s also something we need to clarify as an overarching design goal.

CFML started life simply as a way to get dynamic data into otherwise static HTML pages. It added tags with a specific prefix, which would be interpreted on the server to retrieve data from a database and perform other tasks, as part of generating the final HTML page that was delivered to the browser. It was very much a simple procedural language. It gained user-defined functions along the way, to help with code organization and provide a form of reuse, but it didn’t gain any form of component-based organization until the rewrite on top of Java with Macromedia’s CFMX 6.0 (an effort that had begun at Allaire prior to Macromedia’s acquisition of them). Even in CFMX, the language was not Object-Oriented, it merely had “components” as a way to organize libraries of functions. CFMX 6.1 made it possible to write OO-style code for the first time, but the language was still primarily procedural with some OOP “bolted on”. Claiming it was Object-Based was perhaps more accurate than Object-Oriented.

Many other languages of comparative age to CFML had evolved into much more capable OOP languages by this time – or had even started out life as fully OOP languages (e.g., Ruby). For good or bad, OOP became the way to write large systems by the early 2000’s and that trend continued on over the next decade. Many people in the CFML community petitioned Macromedia, then Adobe, and then Railo (and New Atlanta) – and now Lucee – to make CFML a more capable, more consistent OOP language. The performance aspects around creation of objects kept improving. The syntax for dealing with objects improved (new, scripted-based components, using var at the point of first use). We now have member functions on built-in object types (arrays, structs, queries, etc) and a more natural syntax for iterating over collection-like objects as well. We got interfaces. Some CFML engines support abstract classes, static methods, and so on. We even have ORM built-in.

It is no longer a particularly controversial position to argue that CFML is an OOP language. All the major frameworks and libraries are packaged as classes / objects and often the expectation is that your code extends classes provided by the framework or library. We have (to some extent) adopted a number of common OOP design patterns in the everyday code that most of us write. It took a long time to get here but we are now at a point where many CFML developers are questioning why new functionality is still introduced as top-level functions (or tags!) when member functions, or new system-wide classes with static methods, would seem like a more modern, more sensible approach.

Whilst CFML evolved in a world that had largely switched to OOP in the mainstream and finally “caught up”, we’ve been seeing a new shift in the programming world: the rise of functional programming as a style. Most every CFML programmer who has done any JavaScript has used closures, and now we have closures natively in CFML itself, along with higher order functions like map, reduce, filter. Best practice in many modern languages seems to be leaning more and more toward the functional style and Java 8 saw a huge shift in that direction with a lot of standard library support added for functional style programming in Java to be both more natural and efficient.

I hope that no one reading this wants LuceeLang to be less of an OOP language than CFML. I would hope that, if anything, we all want LuceeLang to be more consistently OOP in style than CFML. I would also hope that at least some of us would like to see LuceeLang better support a more functional style than CFML, taking cues from Java, JavaScript, Swift, Rust, Scala, Clojure, Elm, Haskell, and no end of other languages moving in that direction.

What we decide we want LuceeLang to be – whether it be more procedural(!), more OOP, more FP, or more hybrid OOP/FP – should guide us in the way we approach language design decisions.

If we want more OOP, we should probably view all our top-level functions with suspicion. If we want more FP, we should look at ways to leverage closures more in our “standard library” – and we should look at how we can improve thread safety and simplify concurrent programming in LuceeLang, as those are the main forces driving the recent increase in adoption of the FP style.

A little history: FP is not new. It predated OOP. The early work on OOP was actually done on top of FP languages. The early OOP languages were message-based: you had coarse-grained objects that behaved as actors in your system and they sent messages to each other to effect change in the overall system. FP has continued to be studied and developed but has primarily remained an academic concern until the mid- to late-2000’s, as we started to hit the limits of current systems and needed ways to better leverage concurrency on multi-core machines. What we know as modern OOP, is not what the folks who first designed OOP systems had in mind! Modern OOP leans heavily on shared state, and that is what causes the problems with concurrency. FP favors immutable data with carefully encapsulated mutability where it is still needed: early OOP was similar, with immutable messages and very little shared data.

3 Likes

If we want more OOP, we should probably view all our top-level functions with suspicion.

Indeed, that’s a great question to be had (and one I’m sure you’re hinting at): If we want more OOP, do we want to banish the top-level functions altogether?

My leanings for LuceeLang are for a language that shuns the procedural roots of CFML and tightens its hybrid OOP/FP offering.

2 Likes

Yup. And it’ emininently doable. I did some preliminary investigation on how to package all the existing procedural functions in a more OO way here: https://docs.google.com/spreadsheets/d/1mqiaJRVgh26ljEKgvhb6yf7l6uCKmAmZlIcQqlzh1KM/edit?usp=sharing

I circulated this a while back, but it didn’t yield much interest. Perhaps ppl are now warming to the concept.

Same.

3 Likes

That’s brilliant (and the first time I’ve seen it tbh - too many things going on).

I would like lucee lang to keep the ‘style’ of CFML and incorporate OO & FP options.

CFML has been a mix of tags and script; I think it would be good to give the developer the option of being able to use FP or OO (or a blend) - best of both worlds / best solution for the problem at hand.

I think lucee lang is a good opportunity to remove ‘cruff’ and any backward-compatible nonsense that messed up the language. Pick out the best bits of other JVM scripting languages and implement in a ‘CFML’ way.

Just to clarify, when you say the “‘style’ of CFML”, do you mean all the top-level functions as well? Or do you mean the script/tag approach with member functions supplanting that?

I mean that low-level barrier to entry. CFML has always been easy to learn - is it possible to keep that CFML ‘style’ or perhaps approach would be a better word, when implementing FP.

I am not familiar with the terminology,
but are member functions things like

  • myQuery.filter()
  • myStruct.each()

And top-level functions

  • queryFilter(myQuery, …)

I have just started using .filter() and .each() and am loving it (replacing QoQ).

These two functions accept a function as an argument,
does that makes them

  • closure
  • functional programming (I don’t think so)
  • other??

Is there a Lucee wiki page of terms and definitions?
I have a lot of catching up to do :confused:

@seancorfield has already answered this :wink:
http://lang.lucee.org/t/built-in-functions-vs-members-functions/152

Functions that accept another function as an argument or that return a function as a result are called Higher-Order Functions or HOFs for short. Map, filter, reduce are the staple HOFs but there are lots of other fairly “standard” examples, and anything you write that accepts or returns a function is a HOF too.

Those functions that you pass as arguments or return as results are generally closures: a closure is a function that “closes over” some variables (captures them as part of it’s “body”).

The main characteristics of FP – aside from “functions everywhere” – are purity (lack of side effects) and immutable data (construct something and don’t modify it; create new versions of it with changes).

Hope that helps?

1 Like

I think it would be worthwhile to fix the parameters in them, rather than remove them.

I use both methods in my code for different reasons including readabilty. sometimes a typeNameSomething() is tonnes cleaner than the member functions an vice versa.

I’d probably end up creating an extension if they got removed.