Move internal functions to be OO rather than procedural

I’ve taken the liberty of doing this, as I think it’s an important topic.

I could have sworn this was a topic discussed for Iris, but can only see a tangential reference to it in that doc, wherein one of the voters (presume you, Sean) answered “member” for a lot of options.

One language shortcoming I see in CFML is that prior to some OOness being implemented by adding some member functions is that the code we write as devs was OO, but the language itself was still procedural. This was less than ideal.

Then both Railo and ColdFusion added some “member functions”, which - IMO - might be seen as a reasonable halfway house, but also might be seen as “not completing the job”, and in a way the language ended up being slightly worse rather than slightly better. We no longer had one way to do things, nor did we have two ways (“new/current”, and “preserved for backwards compat”) of doing things: we kinda had 1.5 ways of doing things.

However let’s see the glass as “half full”, and the existing member function work as an “in-production proof of concept”. I think the PoC has been a success, so it’s time to roll out the implementation.

I think if we look at it from a CFML perspective from inside the existing CFML world, the addition of some member functions is good. From the outside looking in, it seems incomplete. Hardly anyone is on the outside looking in, so that doesn’t matter. However for Lucee, it should care more about people on the outside looking in, so it should do this job completely from the outset. I’d even perhaps discuss (separately) never implementing the procedural style functions at all in .lucee. Why have two ways of doing things?

I wonder if I should split this further into different topics, or whether we’ve all got the attention span to discuss multiple linked concepts in one thread?

Topic 1: implement the rest of the missing member functions
Topic 2: only implement the member function model for .lucee


Adam

1 Like

Well, the other thread was really about removing top-level built-ins where member functions make sense. What I had in mind was a thread about grouping the other top-level built-ins at least into “packages” or “classes” so you’d have, e.g., File.open(...) or File::open(...) rather than just fileOpen(...).

I think this is much more contentious because:

  • CFML/Lucee currently has no top-level packages or classes to use as a precedent.
  • You need to introduce rules for how these “built-in” packages behave in relation to regular component/class lookup for static members/functions. I think that’s going to lead to a complex cascade of import package rules etc – and that is a tarpit in most languages that have gone down this path (happy to discuss the subtleties in detail if we do go this route).
  • It’s more typing.
  • Unless you introduce some way to have with scoped usage: with File { ... open(...) ... } and I’m not sure that is a “good” language feature at all (it’s proved problematic in several languages that have gone down this path).

My personal feeling is that it’s not worth all the additional baggage and complexity it brings in: by all means remove top-level built-ins where there’s an equivalent member function; and implement more member functions where it is obvious how to do so; but stop short of “full OO” here.

I agree with packaging the language functions and classes into namespaces. It can help with naming:
ORMEvictEntity()

import lucee.lang.ORM;
evictEntity(); //flushes the session
getSession(); // gets orm session

I think it would be helpful. You can sort of do this with non-built-in functions and objects with import and include.

My 2 cents. Maybe there are better ideas.

Just to note that the more common import scenario in other languages is this:

import lucee.lang.ORM;
ORM::evictEntity();

To do what you want would need either a wildcard import or specific named imports:

import lucee.lang.ORM.*;
evictEntity();
getSession();

import lucee.lang.ORM( evictEntity, getSession );
evictEntity();
getSession();

This is what I meant about the baggage and complexity because you get into additional “scope” rules and name hiding, and how built-in types, packages and functions all interact with user-defined packages (classes) and functions.

Definitely doable, but importing packages etc is a “big” language feature, and one that many language struggle with for years.

I personally dont mind the top level functions - although I wish they were implemented in the same way as user defined functions - so that we could pass them around.

If we go the way of importing I’d hope that we would have the ability to define our own packages, and have the members of those packages overlap with existing built in functions, so that we could augment or fix existing BIFs.

In regards to member functions, id also like the ability to override or augment member functions on the different types - id love to be able to define push() and pop() on arrays for instance. or to change how array.contains() works.

1 Like

If something needs to be “fixed” we should identify it and fix it in Lucee lang :smile: If package names are a thing, we should all be doing the same thing rather than going our own way.

So, File::open() would be kind of modelled along the Lucee 5 “static” concepts. From a syntactical point of view I’d like that and I think it’d be something people could get used to quite easily.

Your second bullet point is my biggest worry in going down this path. I assume it would mainly end up becoming a nightmare if the was treated like a proper LuceeLang compontent. But would it be possible to just restrict other components being named “File”? That’s obviously an ugly sledgehammer solution though.

The your 4th point — you wouldn’t have any source to read up on re issue with “with” in other languages? I’d be keen to learn more on the problems that could cause.

I thought things like file would just be ‘available’
as too ORM - but maybe via an extension
so I would not need to import - just something like this:

myFile = new file(); // or lucee.lang.file??

myFile.open(...);

The really nice thing about TAGs which I think should be kept in script is how it handles construction and deconstruction. For example, with file <cffile action=“read” …> opens, reads and closes the file for me.

So in script, I think I should be able to do

myFile.read(...);

and it behaves the same as the TAG

But also have low level calls, so I I do .open() I am responsible for .close()

I could be missing some significance of import:

  • it makes for a lighter request - only loads what is required??
  • better for compiler??
  • better debugging??

If this is the case I would appreciate a quick explanation

1 Like

I wish arrayContains() returned a boolean instead of an index. I dont like the function signature for several functions. I dont understand why dateFormat() cant work just like dateTimeFormat() (it almost does). “Fix” is sometimes subjective.

The abilitiy to have our own packages or augment existing functionality means that libraries can come around and actually show you how things could be done better - or prove that they aren’t better.

I would much rather see time spent right now expanding the meta-programming and extensions of the language rather than baking new features into the language itself - then let the libraries shake out what is and isn’t a good idea.

1 Like

Unfortunately Googling for analyses of why “with” is worse than the problem it purports to serve is extremely different (Google with block scope for example and see how far you get :wink:).

Some interesting discussion on “with” blocks can be found on the C2 wiki: http://c2.com/cgi/wiki?WithBlockCodeSmell (and various linked pages). This is part of Ward Cunningham’s site by the way (the creator of the first wiki, and long-time expert in design patterns and extreme programming).

Part of the issue is that “with” blocks tend to encourage a style of programming that is itself a code smell (doing large blocks of low-level operations on a single object or class type), as well as the general problems introduced by name hiding and shadowing.

In that case this is probably the right time to raise discussions about functions that either have nonsensical return values or function signatures, perhaps other people either haven’t thought about it or they have the same issues but haven’t said anything :smile:

Is there a particular thing you would like to do that can’t be done currently with a component? For example you could certainly write a component that bundles up your date/time related methods. Is the problem better “global access” to those methods? Maybe Lucee 5’s static methods can already help in this regard or maybe you’re thinking of something beyond that.

I agree - was going to post the same thing here. A clear and easy way to extend and override the core language would be welcome. Moving internal functions into static methods on core classes might be good opportunity for that.

Providing compatibility layers, for instance. You might be converting from BlueDragon.net and need a quick way to solve some compatibility issues - installing an extension that supplies core overrides might be useful.

First pass of seeing if all BIFs can find a sensible package to live in here: https://docs.google.com/spreadsheets/d/1C_w70ohIus0b0bczTau0EpPvSEBN_2LjvBNFCL4TIvw/edit?usp=sharing

NB: I only put a “Sunday morning” amount of effort into this, and wasn’t very careful in places, so might not be entirely correct / sensible (and the “Static” column if def incomplete). Also some functions I’ve never used, so don’t have a good handle on their usage, so might have got the potential package completely wrong from a usability POV.

But I found a not-too-daft(?) package for everything.

The doc is read/write for anyone with the above link.

Thoughts?


Adam

2 Likes

I have no way of implementing my own member functions on the different types.

Even in a component I cannot override a BIF using the same name. I just tried this and apparently you can use BIF names in components now - I thought I had tried this recently and couldnt - is this a new change? I admit this does open a few possibilities I haven’t previously considered.

A great use case.

Re: list functions - with a few new functions added to array, could we not get rid of most of them and expect that you will just use something like String.split(delim) to convert any list to an array to do your work and then you can turn it back into a list if necessary? Add arrayFirst(), arrayLast() and arrayRest() (please do these anyway!) and you get 90% of the list function usage ive ever needed.

I’ve started to leave comments and fill in some of the blanks in red. Don’t have more time now, but will have another look at it later.

Question for you though: In package, sometimes you list “Math” or “Number” or “String”, sometimes “lucee.lang.Image” — is that just meant to be inconsistent in a particular way or does it just need cleanup in the doc?

Very first “Observation” cell (E2):

Basically I thought “core” type stuff goes in lucee.lang, less core stuff goes “elsewhere”, and I started by just suggesting “lucee.util”. That part of it was not a very fully formed idea, but I figured some sort of differentiation there might be useful. Or not. Dunno :wink:


Adam

I thought Railo/Lucee had always been this way and it was only Adobe that borked this so badly, but I may be wrong?

IIRC you still have to prefix calls with this. or variables. (or someObj.) but that’s fine IMO. Adobe was just plain wrong to have BIFs collide with CFC methods and folks told them so from day one…

Ah, ok - get it now.

Cool, there’s a candidate for discussion, and it probably deserves it’s own topic :smile:

To be honest’ i have not read anything in detail, so if I miss something, sorry.
In my opinion this is not about “procedual” vs. OO, this is about having a prefix for functions or not and give them a “modern touch”, there are cases where it make sense to have some kind of prefix and some not.
So don’t get me wrong, i’m not against it, i think it is a good approach.
We already have the tools to make this happen, we don’t have to reinvent the weel for this.

“System” components

Lucee comes with a couple of components (org.lucee.cfml) that are auto imported into every template, so you don’t have to do import them manually (same as package java.lang in java).

in your code you can easily do:

http=new HTTP();

Of course with support of the static function in Lucee 5 you can also extend this componens and support things like this:

HTTP::whatever(value);

So we can easily extend this existing components with new (static) functions and also add new components.
Everybody can do this right now, simply extend the components add /lucee/components/org/lucee/cfml.

what we could also do is some base functions (in Base.cfc), so that every of this component has this opportunity:

HTTP::doc() // shows the documentation for this function

Maybe even

Documentation::dump();// print out the doc for all components
Documentation::dump("FTP");// print out the doc for FTP only

That is the biggest benefit i see in this, we can improve visibility.

1 Like