Lucee 6 - Annotation

We are thinking about introducing annotations in Lucee 6, they then for example can be used to mark parts of your code as deprecated, define a component to implement/extend a certain java interface/class or you can define your own custom annotations.

So first of all, do you like the idea at all?

I personally hate to have annotations in doc comments like this

/**
 *@java {extends:"java.util.ArrayList"}
*/
 component {
}

this are doc comment tags and not annotations.
I don’t like this because a doc comment should only hold meta data, but not data that influence the runtime or the compiler in any way.

So i personally would prefer, to do it the same way as in Java as follows

@java(extends:"java.util.ArrayList")
component {
}

any input?

2 Likes

I would also prefer the second, I found some Kotlin links to study:
https://kotlinlang.org/docs/reference/annotations.html
https://youtrack.jetbrains.com/issue/KT-25947

Is this still coldfusion compatible or would I need to remove these lines to get it working with coldfusion?

You would have to remove them from ColdFusion.

I think, it is great to keep compatibility between Lucce CFML code and
Coldfusion CFML code.
Then having a program witch could align the code :
from CF to Lucee or from Lucee to CF

could be something to think about, if possible, not too complex and with no risk.

Then there will be no movement from lucee

As this will be pretty edge case functionality. I don’t think this is possible if ACF with any syntax? I would prefer the (breaking) java compatible syntax.

J

Generally speaking, annotations can already be specified in two different syntaxes. Both of which present themselves the same in the component or function metadata.

component singleton foo=bar {
}

and

/**
* @singleton
* @foo bar
*/
component{
}

And now we’re discussing the possibility of adding something like this as a 3rd option:

@singleton
@foo bar
component{
}

I’ll say the Java-esque way is a little cool and I can understand people who don’t like putting compiler directives in comments, but think we’d better have a darn good reason for adding a 3rd syntax into the language for doing the same thing for which we already have 2 working solutions.

After thinking on this a bit (and discussing it internally with @micstriit and @lmajano I’m included to stick with the 2 existing options as they

  • Are compat with Adobe CF
  • Naturally transfer to tag-based CFC and UDF declarations as attributes.
  • Allow people to use the first syntax if they don’t like the “java doc” comment approach.

I do realize that there are a few limitations of what characters are allowed in the existing approaches. For instance, Lucee can’t handle a colon in the annotation name since it leaves ambiguity. I assume some similar rules would apply to the newly-suggested approach as well.

So to address the original example Micha gave-- it came from a discussion about allowing CFCs to directly extend Java classes. My preference would be able to prefix java: in front of a class name to differentiate between a CFC or a java Class. i.e.

oUser = new path.to.CFML.User();
jThread = new java:java.lang.Thread();

I’ll note that the above syntax was suggested 3 years ago in [LDEV-116] - Lucee and Adobe originally advertized they were going to support this, but then changed at some point and never replied when I asked them about it.

Anywhoo… building on that concept above which I really like for creating Java objects, it could be extended straight to our existing syntax like so:

component extends="java:java.util.ArrayList" {
}
4 Likes

I do not see the benefit of having a third option for this. Can someone elaborate further on why the third option is better if we already have 2 that work?

@bdw429s you seem to confuse different things here

what Lucee support so far are doc tags within doc comments

/**
* @return whatever
*/
 function test() {
}

That has nothing to do with annotations

@deprecated // will maybe output a warning in the debugging
function test() {
}

We are not talking about a different syntax for existing functionality.
Doc comments simply give structured info to the metadata you provide.
Annotations allow you to influence the language on compiler or runtime time on a new level.

My failure was to begin that discussion about the possible syntax of annotations what is the least important thing.
I should start the discussion about annotations in general. my problem is that im sold on annotations for a long time :wink:

Annotation are a very powerful tool and used right could make a lot of things easier in Lucee.

For example

  • Rest Webservices Mapping
  • Soap Webservices Mapping
  • ORM Mapping
  • Java Mapping
  • debugging
  • json mapping

ATM components have tons of attribute for all possible mappings (see bullet points above) that could be much easier with annotations.

Another miss-understanding seem to be that wen you do

@java (implements:"Whatever")
component {
}

that you then can do something like

new java:MyWhatever();

in that case the annotation only tells Lucee as what Java interface that component can be wrapped. It does NOT make it a Java class, so the following would be something different.

component implementsJava="whatever" {
}

in that case the component holds the “contract” the Java interface defines and also can be used as such.

the annotation tells lucee how to handle the component in specific situations.
an other example could be

@rest("/my/rest/path")
component {
}

BUT the really powerful thing with annotations is that you can define your own (Java or CFML based).
you then get injected within the compiler or the runtime and can influence that process.

So adding support for example GraphQL to Lucee could be done by your own annotations you write in CFML and then can be used like this

@graph(path:"/aa/dd/",whatever:true)
component {
}

you seem to confuse different things here

I don’t think I’m confusing them at all. But I am asking why they should be any different. So far as I’m concerned, the “java doc” syntax is just an extension of the mere notion of addition attributes to your cfcomponent tag or component declaration. You say it has nothing to do with annotations, and I’m challenging that assertion by saying “why?” I’m trying to avoid any assumptions from Java (which doesn’t have a concept like CFML’s annotations) and see what’s idiomatic in CFML.

If our goal is to be able to add additional text in the vicinity of the component or function declaration that will be visible in the metadata and possibly influence the compiler, then it seems CFML already provides that in a manner that works with both tags and script.

That has nothing to do with annotations

Annotations in the suggested format do not exist at this point in CFML so therefore it’s up to us to decide what it has to do with or how they work.

We are not talking about a different syntax for existing functionality.

You aren’t, but I am :slight_smile: I always think language discussions should start with the existing mechanisms and ask why those can’t be used.

Another miss-understanding seem to be that wen you do…

That was never my understanding. I’m actually pushing for the extension and interface implementation at the Java level to NOT have any sort of new syntax implemented, but instead use the existing extends and implements syntax of the language with a java: namespace. I’m aware of the differences between extending and implementing though.

component implementsJava=“whatever” {}

I’m saying that should instead be:

component implements="java:whatever" {}

BUT the really powerful thing with annotations is that you can define your own (Java or CFML based).
you then get injected within the compiler or the runtime and can influence that process.

How is this different from what I can do today by adding attributes to a cfcomponent tag.

<cfcomponent extends="foo.bar" brad="wood">

or in script as:

component extends="foo.bar" brad="wood" {}

@rest(“/my/rest/path”)

But why not just this:

component rest="/my/rest/path" {}

I’m just failing to see the benifit. At the end of the day, it feels like we’ve just slightly modified the existing approach.

@graph(path:“/aa/dd/”,whatever:true)

Why not this:

component graph-path="/aa/dd/" graph-whatever=true {}

Basically, I feel like CFML has copied too much from Java without asking if it is truly idiomatic to CFML. I’m still trying to figure out exactly what the new syntax would give us. Since we control what Lucee does, we can make any syntax do anything we wish. So at the end of the day, it just boils down to the same functionality being available in a different syntax. So far as I know, Java does not have the ability for developers to add arbitrary things to the definition of a class, so the Java style of annotations is used for that. CFML has always allowed this as arbitrary attributes you can add so I don’t feel that the Java style needs to be added to CFML.

In retrospect, the java style may allow for some better organization and syntax, but I feel CFML is already in bed with an existing syntax for this. Whether you use the comment style or just the attributes right there in the component declaration, I’d like to see us focus on exactly what doesn’t work in that syntax as an impetus for looking at a new one. We can make any syntax do whatever we want, so it’s really just a syntax discussion at the end of the day to me.

Sorry you still miss my point. I will try to explain myself better :wink:

First forget the word Syntax, as i have written before the syntax does not matter at all.

Let me try it again with one example.

this

component implementsJava="Whatever" {} // Syntax A
...
component implements="java:Whatever" {} // Syntax B
...
component java={implements:"Whatever"} {} // Syntax C

defines that this component needs to implement the Java Interface “Whatever”, this is checked at the same time a regular “implements” is checked. the only difference to a component interface is how the interface itself is read, otherwise it is exactly the same.

On the other side this

// Syntax A
@java(interface:"Whatever")
component {}

// Syntax B
/**
* @java {interface:"Whatever"}
*/
component {}

// Syntax C
/**
* @interface Whatever
*/
component {}

Is something completely different, Lucee does not care about this at all when loading a component, this “annotation” only come into play when you try to convert that component to a certain java class, for example by passing to a Java method that looks like this

void test(Whatever w);

or you do

javaCast("Whatever",cfc);

So you can use that component as a regular component and the annotation only comes into action, when you use that component in a specific way.

So what is the difference between the existing “doc tags/undefined attributes set” and my suggested annotations?
doc tags never influence the flow of the language itself unless you read them and act based on your own logic.
Doc tags are completely stupid and have now logic on their own. Instead of using doc tags you could also use a file name pattern and achieve the same goal
So instead of

component bean=true {
}

you could also do

AddressBean.cfc

and based on the File name make certain actions.

How COULD annotations work?
Let me try to make an other example (please have in mind that this is just an idea how annotations could work in Lucee i just made up now)

Let’s go back to this example

component bean=true {
}

as i said, Lucee does not care at all about that attribute at any time, with annotation i can make Lucee care.
You could for example do something like this in the Application.cfc/Server.cfc/Web.cfc

this.annotations.bean={
   type:"component"
   retention:"instantiation"
   action:function(attrValue,???){???} // could also be a Java Lambda
}

then the function defined under “action” get called when the component is instantiated and can influence that process.

Let say we only teach Lucee annotations but nothing else, so Lucee has no clue what the following is and does not care (simply metadata)

component java={implements:"Whatever"} {
}

You could add this to the Application.cfc

this.annotations.java={
   type:"component"
   retention:"casting"
   action:function(attr,cfc){???;return createDynamicProxy(cfc,attr.implements)} 
}

In that case we wrap the component with a proxy class when passed to a Java method

this would give you far more influence into the language as you have today, as i have written before and this has nothing to do with adding a new syntax for something that already exists.
Actually we do not necessary to have do add a new syntax at all. It’s all about the possibility to inject your code into specific actions Lucee does.

My goal is not to blindly adapt something from Java, the hole “define a Java interface for a component/convert a UDF to Java Lambda” made me thinking about how in CFML this is handled in general today.
How do we tell a component that it also can be used as a webservice endpoint (Rest and Soap) , a Hibernate mapping or how we convert a component to a json string. Is CFML/Lucee consistent on that and what would be the logical approach to add support for a java interface or Java Lambda mapping?
This raised the question for me, “could annotation” be an approach for all of this?
It is all about the greater picture.

sidenote:
It is confusing today in CFML that you can define metadata via tag attributes and doc tags, that would raise the question with for example this

component rest=true restpath="/susi" {}

for me that is a backed in annotation the more logical syntax would be

@rest true
@restpath "/susi" 
component rest=true {}

or if you prefer

/**
* @rest true
* @restpath "/susi" 
*/
component rest=true {}

(syntax does not matter)

But of course we cannot change that.

Hope this makes now a little bit more sense.

2 Likes