isExists() opposite of isNull()

Just a random idea, I would love to see something like isExists() or isPopulated() of isValue() as being an opposite to isNull(). I just think it would read nicely in a lot of code. I was just thinking of this when wiring-together a dynamic query (pseudo code):

function update(
	required numeric id,
	string foo,
	string bar
	) {
	
	```
	<cfquery>
		UPDATE
			record
		SET
			<cfif isExists( foo )>
				foo = <cfqueryparam value="#foo#" />,
			</cfif>

			<cfif isExists( bar )>
				bar = <cfqueryparam value="#bar#" />,
			</cfif>

			updatedAt = UTC_TIMESTAMP()
		WHERE
			id = <cfqueryparam value="#id#" />
	</cfquery>
	```

}

Right now, I accomplish the same thing with:

<cfif arguments.keyExists( "foo" )>

But, I think the isExists() would just be a nice convenience.

I know that CFML has had isDefined() forever. But there’s something about that function that always felt janky to me. I think because it used “Strings” at least in the very beginning. It might be more modernized now? I’m not sure.

Anyway, just thinking out loud.

1 Like

Do people use isDefined()? Maybe my distaste for it is outdated.

Just looking at the isNull() implementation in Lucee’s GitHub:

public final class IsNull implements Function {
	public static boolean call(PageContext pc, Object object) {
		if (object == null) return true;
		if (object instanceof QueryColumn && NullSupportHelper.full(pc)) {
			return ((QueryColumn) object).get(pc, null) == null;
		}
		return false;
	}

	// called by modifed call from translation time evaluator
	public static boolean call(PageContext pc, String str) {
		try {
			return pc.evaluate(str) == null;
		}
		catch (PageException e) {
			return true;
		}
	}
}

Looks like the meat of it is:

pc.evaluate(str) == null

I think what I’m looking for is a Function that is essentially a ( ! isNull() ) call. Which seems to be much different than the isDefined() implementation:

public final class IsDefined implements Function {

	private static final long serialVersionUID = -6477602189364145523L;

	public static boolean call(PageContext pc, String varName) {
		return VariableInterpreter.isDefined(pc, varName);
		// return pc.isDefined(varName);
	}

	public static boolean call(PageContext pc, double scope, Collection.Key key) {
		try {
			Object coll = VariableInterpreter.scope(pc, (int) scope, false);
			if (coll == null) return false;
			Object _null = NullSupportHelper.NULL(pc);
			coll = ((VariableUtilImpl) pc.getVariableUtil()).get(pc, coll, key, _null);
			if (coll == _null) return false;
			// return pc.scope((int)scope).get(key,null)!=null;
		}
		catch (Throwable t) {
			ExceptionUtil.rethrowIfNecessary(t);
			return false;
		}
		return true;
	}

	public static boolean call(PageContext pc, double scope, Collection.Key[] varNames) {
		Object defVal = NullSupportHelper.NULL(pc);
		try {
			Object coll = VariableInterpreter.scope(pc, (int) scope, false);
			// Object coll =pc.scope((int)scope);
			VariableUtilImpl vu = ((VariableUtilImpl) pc.getVariableUtil());
			for (int i = 0; i < varNames.length; i++) {
				coll = vu.getCollection(pc, coll, varNames[i], defVal);
				if (coll == defVal) return false;
			}
		}
		catch (Throwable t) {
			ExceptionUtil.rethrowIfNecessary(t);
			return false;
		}
		return true;
	}

	// used for older compiled code in ra files
	public static boolean invoke(PageContext pc, String[] varNames, boolean allowNull) {
		int scope = VariableInterpreter.scopeString2Int(pc.ignoreScopes(), varNames[0]);

		Object defVal = allowNull ? Null.NULL : null;
		try {
			Object coll = VariableInterpreter.scope(pc, scope, false);
			// Object coll =pc.scope((int)scope);
			for (int i = scope == Scope.SCOPE_UNDEFINED ? 0 : 1; i < varNames.length; i++) {
				coll = pc.getVariableUtil().getCollection(pc, coll, varNames[i], defVal);
				if (coll == defVal) return false;
			}
		}
		catch (Throwable t) {
			ExceptionUtil.rethrowIfNecessary(t);
			return false;
		}
		return true;
	}

	// used for older compiled code in ra files
	public static boolean call(PageContext pc, double scope, String key) {
		return call(pc, scope, KeyImpl.getInstance(key));
	}

	// used for older compiled code in ra files
	public static boolean call(PageContext pc, double scope, String[] varNames) {
		return call(pc, scope, KeyImpl.toKeyArray(varNames));
	}
}
1 Like

Even knowing that isDefined() should be avoided (the docs say it should be considered as deprecated) I sometimes use it because in my apps I switch session (and cookies) dynamically, and until a session isn’t started dynamically, there won’t be any session scope. There won’t be any scope struct named session. Then, any conditional with structKeyExists(session, "someVar") would throw an error. Of course I could use cftry and cfatch as an alternative, but I’d rather rewrite that few lines of code as soon as isDefined() gets pulled out from being supported.

I think in that case, isDefined() makes sense. That feels like a valid use-case for it.

Really, what I am talking about is when I have a variable that may or may not be populated. It’s mostly similar, mabye I am making a difference without distinction.

What’s about isEmpty?

<cfquery>
UPDATE record
SET 
  <cfif not isEmpty( foo )>
    foo = <cfqueryparam value="#foo#" />,
  </cfif>

It’s quite similar to php’s empty function, which always feels pretty natural to me.

However, about the isDefined discussion, I’m always trying to give default values to parameters or arguments instead of using isDefined which seems to me a bit of code smell. I don’t like to deal with this kind of uncertainty.

Anyway the uses that are described in comments are all prety fair and common imo. I’ve found myself in some situations where isDefined seems to fit perfectly.

1 Like

I completely forgot about the isEmpty() function - not sure I’ve ever actually used it before. I wonder what it does with null values, I’ll have to try it out.

Re: giving default values, I definitely do that sometimes. It just depends on the case. With cfparam, I’ll default for optional values. And with cfargument, I’ll default for optional arguments - in many cases. The part where a default falls down in my experience is with Update/Filtering functions where there is a difference between “default” and “undefined”. For example, in a Filtering function, I don’t want to use an undefined value to filter something.

Right. I’ve been digesting this. My thoughts.

  • isExists isn’t valid English: it’s verb + verb (parallel: “is swims”). isExistent, maybe. But just no.
  • exists would work as a method (someObj.exists(someElement)), but I’m not sure it works as a function (exists(someObj.someElement) or exists(someObj[someElement]). It’s not awful though.
  • isPopulated. Not sure “populated” is a term generally used in this sort of context. No.
  • isValue. Doesn’t make sense in the context. Neither someObj.isValue(someElement) nor isValue(someObj.someElement) make sense, because a parallel in the same vernacular would be someObj.isInteger(someElement). We know what an integer is, but what does “value” mean here? It’s asking the wrong question. No.
  • hasValue. Fixes the above problem. This only works if we decide null is not a value (it’s an absence of a value). That’s probably accurate for CFML I think. As much as CFML bothers to rigorously define such concepts. Maybe.
  • isDefined has a bit of a crap implementation as it only works with a string containing syntactically valid variable names. So doesn’t work with anything other than dot notation. Not equivalent.
  • parameterExists… exists… but is deprecated in CF (but not in Lucee I note). It already does what you’re asking for, but has a bit of a rubbish name. No, only because of the CF deprecation.
  • isEmpty is doing the reverse of what is being asked for. Not valid.

How do other “similar” languages positively check for a variable having a value?

  • Ruby: defined?(something).
  • PHP: isset($something).
  • Python: actively not catered for (the thinking being: don’t write code that needs to check this sort of thing).
  • JS: no general positive way that I can see. hasOwnProperty works on object properties.

Of all of that lot, exists seems to be the best to me.

However it kinda seems like busywork to me. The occasions where !isNull(something) is unclear are infrequent, in my book; and generally easily solvable anyhow. I understand the notion that negative expressions are harder for brains to process, and “not is null” is kind of a double negative in a way, but I don’t really think this idea adds much to the language.

Also: as per usual I don’t believe Lucee adding stuff that CF doesn’t have is improving the CFML situation, in general. If Lucee devs thought they were running out of work to do; it would be a better use of their time to look at stuff already labelled as acf-compat in Jira, before adding new ones.

Interesting thought exercise nevertheless, Mister Nadel. Cheers!

3 Likes

Very thorough response! In JavaScript, you don’t really need a special function for this since JavaScript already treats anything as a Truthy/Falsy when testing conditionals. Though, undefined reference errors can be thrown in certain circumstances.

Yeah, I guess I’ll try giving ! isNull() more of a chance. The problem with null values is that I can’t just make my own implementation because null gets handled special by the CFML compiler/runtime. So, any attempt to make my own exists() function will simply throw a Null reference error when I try to test a null value :confused:

Yeah I tried to create a NOT.cfc which had an onMissingMethod that would simply proxy to the original CFML function and then ! the result and returning that.

But:

  • CF (unsure about Lucee) would not allow a CFC called NOT as it’s a reserved keyword. So I tried N0T (that’s a zero) instead but…
  • … as you say: myNot.isNull(somethingNull) errors out saying “it’s null you jerk, you can’t do that”. Sigh.

Still wondering whether the absence of onMissingStaticMethod is something that CFML should address, btw. But did not research it beyond going “dammit” (which is not really “research” ;-)).

1 Like

I have yet to play with any of the static stuff in CFML. Pretty much everything I have is instantiated and cached.

I’m curious as well, so let’s do some tests:

<cfapplication action="update" nullSupport="true">
<cfscript>

labels = ["null", "false", "empty string", """0""", "0", "{}", "[]"];
values = [ nullValue() , false, "", "0", 0, {}, [] ];

labels.each(function(label, index){
    if(isEmpty(values[index])){
        writeOutput("<p>(#label#) is empty</p>");
    }else{
        writeOutput("<p>(#label#) is not empty</p>");
    }
   
});
</cfscript>

I’ve tested this code in https://trycf.com/ with lucee engine

Results:

(null) is empty
(false) is not empty
(empty string) is empty
("0") is not empty
(0) is not empty
({}) is empty
([]) is empty

Quite interesting indeed, if you’re using only Lucee, you can enable nullSupport I guess, and solve all these kind of issues with null.

Note that false is not empty!!!

Try to disable you’ll find an interesting error so far!

2 Likes

I’m so curious to try enabling null support in an application! I’ve had it on my list of things to try forever!!

1 Like

Yes they are.

Do people use isDefined() ?

I do, using it, as follows:

isDefined("fully.qualified.name.of.variable")
1 Like