curious what people's thoughts are on this acf 11+...
# lucee
w
curious what people's thoughts are on this acf 11+ vs lucee incompatibility, how SHOULD it be? https://trycf.com/gist/0226ff781f4a12b8190768ac2a5e24db/lucee5?theme=monokai
b
There is a ticket for this
It's a long-known issue that I've railed against for years for both Adobe and Lucee to fix
Adobe eventually listened, Lucee still has it in their backlog 😕
w
to fix in which direction?
b
For both versions to work
Copy code
BIF( value ) // Doesn't error

value.bif() // also shouldn't error
w
roger, thanks
b
Though I understand the underlying JVM underpinnings of why it doesn't work, CFML itself really only has "simple values" .
The CF community is not a monolith on this, so you'll find various takes. I understand the argument against it, I just think the member functions are stupidly useless when I can't even do
Copy code
if( isSimpleValue( foo ) ) {
  foo.len()
}
There is no
isString()
in CFML so to have some "simple value" member functions that don't work leaves us stranded by design
And from a framework designer, I regularly have methods which can receive values from the user which I have zero control over and have a limited set of cross-engine ways to test them to see if they are REALLY strings or not. Which leads me to sadly avoid a lot of the "simple value" member functions since I can't count on them to work!
I stand corrected regarding Lucee and the backlog-- looks like they've addressed this in lucee 6 perhaps! https://luceeserver.atlassian.net/browse/LDEV-332
That is great news. I just hope to see Lucee 6 before my kids have kids 😆
a
I'd really really prefer it if ppl could learn the difference between
someFunction(canTakeManyTypesAndDealWithThem)
and
aSpecificType.aMethodOfThatType()
. In this example although there's two things called
len
, it's just dumb to think they're the same. One is a function that is implemented to take a
SimpleType
, the other is trying to use a method
len
on a
Numeric
object. This is not complicated. @bdw429s I'm kinda with you with the "make a sort of
SimpleTypeInterface
that all simple types in CFML implement". But it still seems like a bit of a hack when just "devs conceding they're having a brain fart" would be a better solution I think. Does it make any sense, for argument's sake, that
someDate.len()
returns 26? What's the length of a date? Is that a even sensible behaviour to ask about? I mean it does make sense that
len(someDate)
returns 26 cos intrinsically it's clear that the
len
implementation needs to convert the date to a string before before checking the length of that string. But does it really make sense that the operation of
someDate.len()
- and method of the DateTime type, remember - should "convert"
someDate
to a string before calling
len()
on it? I don't really think it does. All this justification of
Copy code
BIF( value ) // Doesn't error

value.bif() // also shouldn't error
isn't really that sensible to me. It's just pandering to devs not engaging their brain. Or... understanding basic programming concepts. I know we set the bar very low for CFML devs, but does it need to be that low?
(to answer your question, @websolete, I think Lucee is correct here)
Maybe if one was to think that
len(String)
,
len(Numeric)
,
len(DateTime)
etc are actually all variants of
SimpleObject::len
, and
SimpleObject
is automagically imported behind the scenes. But I guess it'd take longer to explain that thinking to a CFML dev than trying to explain
function(obj)
is not the same as
obj.function()
. Sigh.
w
not sure i had any specific expectations whether numericVar.len() working one way or the other. i was iterating a struct in lucee and was checking .len() on each key's value; it bombed on the first numeric value it came across. i checked other cf versions in trycf and it was apparent that acf, through whatever means, DOES think .len() should work the same as len(). was just curious whether that should set expectations that other member functions in acf will behave similarly when there's a datatype 'mismatch'. i actually explicitly rewrote this code not long ago from bif() to .member() to see how well i liked using all modern syntax available in lucee; it had been working before that rewrite, this issue just caught me offguard
b
I'm with Adam that it would be very nice to have CF's native type actually be some sort of documented interface or class. Any other language I've seen with member functions on data types have a formalized definition of what methods to expect as well as a first class citizen language construct to detect those types.
The issue here IMO is less of, "Oh look at those CF devs, too stupid to understand OOP" which is somehow where this discussion seems to go. It's that the design of CFML itself literally provides no reliable mechanism for generic code to test values in a way that can ensure the member functions that can be expected.
Consider these examples. There is nothing at all wrong with this code, it uses the built-in type checking provided by the language, yet the member functions attributed to those types don't exist.
Copy code
s='5'
if( isNumeric( s ) ) {
  dump( s.max(3) )
}
Copy code
n=5;
if( isSimpleValue( n ) ) {
  dump( n.trim() )
}
Copy code
c = new Query();
if( isStruct( c ) ) {
  dump( c.keyList() )
}
You could easily write those in any sort of code iterating over an unknown struct and the code will even work for many values. But each of those error. I believe the technical term for that is "crap language design". 🙂
As I've said before, CF is not a language that says, "this is an instance of `cfml.lang.Numeric`", or even "this is a _number_", it says, "this can be used as a number". Therefore it is idiomatic IMO to CFML that
Copy code
thingThatCanBeUsedAsNumeric.numericMember()
should be a safe line of code to write given the current design of the language.
The next best thing (even though there's no precedent for explicit casting in CFML outside of Java calls) would be something like
Copy code
thingThatCanBeUsedAsNumeric.castNumeric().numericMember()
or
Copy code
castNumeric( thingThatCanBeUsedAsNumeric ).numericMember()
though at this point, you've typed more than this! 😕
Copy code
numericBIF( thingThatCanBeUsedAsNumeric )
a
this frickin' gobsmackes me:
Copy code
c = new Query();
if( isStruct( c ) ) {
  dump( c.keyList() )
}
Well. It really doesn't I guess. I just rolled my eyes and went "well of course CFML does that [f***in stupid language]".
b
Anyone who's written any sort of generic datastructure iteration such as recursing over an object that can contain structs, arrays, strings, numbers, etc has run into that sort of thin many times as soon as something you didn't expect was present.
Oh, and to address a question of yours above
Does it make any sense, for argument's sake, that
someDate.len()
returns 26?
Yes, I believe it does. In fact, I literally wrote that code last week when formatting console output in CommandBox I needed to know the exact number of chars in a date to output to the screen to calculate the width. I mean, that's not even a stretch! If it can be output as a string, it has a length. I even did the same thing where I needed to know the length of a number (again for formatting/width purposes) and I had to use
len( YAxisMax )
instead of
YAxisMax.len()
because I wouldn't know if the user of my code would be passing their value as a string or a numeric.
a
I'm sooo split on this:
Copy code
thingThatCanBeUsedAsNumeric.numericMember()
I 95% get where you are coming from. Well I 100% get where yer coming from. Just not 100% convinced yer right. That's the 95% It comes back to whether one should consider
function(object)
should intrinsically be considered analogous to
object.function()
. I don't think it needs to be. And your - entirely reasonable point - about
thingThatCanBeUsedAsNumeric.numericMember()
presupposes it is analogous. However CFML is already a clusterfuck of a language, so I guess I don't see why CF and Lucee shouldn't implement
thingThatCanBeUsedAsNumeric.numericMember()
and
anySimpleValue.analogToFunctionThatTakesASimpleValueWhetherItMakesSenseOrNotOnTheObjectItsBeingCalledOn()
don't exactly make things worse. I s'pose. And I also take yer point that it's all well and good teaching the CFMLers to know their objects from their elbows, but this does not help framework code that needs to use generic code. Other than the fact why don't you just stick with
function(object)
as that is the already-existing generic CFML handling of such things. Nothing's forcing this generic code to use woolly-type-specific methods when there's a generic function already?
b
Chaining is the main thing making me want to use them, but that normally is when I'm using struct or array member functions, which don't have near the ambiguity that simple values do.
a
I also think I'm being dogmatic / perfectionist (for a subjective definition of "perfection") about things, and at the end of the day, what yer suggesting @bdw429s would be helpful and save confusion.
Do I really hate that one could do
itIsABloodyNumericFfs.len()
(for whatever reason, it could be valid), and get back the length of the numeric value if it had
.toString()
called on it? I guess not.
myDate.uCase()
..? Well if that's what you decided to do... knock yerself out. I guess.