Ok, so I’m ready to pull the trigger on one of our many departures from CF in BoxLang and I want to solicit a bit of feedback before proceeding. One of our driving goals of the 2-parser design and transpiler was being able to “fix” the warts of CF that we wish had been done differently. (I’m not throwing CF under the bus here-- BIFs long predated member methods, but if we were to write it again from scratch today, there are different decisions we would make!)
So the idea here is that any BIF (Built In Function) that operates on a data type would just be a member method if we were to design a language from scratch. This is how most other languages work and is pretty familiar outside of CF. I’ll also note, BoxLang doesn’t have any of the issues that plagued CF and Lucee for years where member methods wouldn’t always work on all data. Like how len( true )
was valid, but true.len()
would error. CF/Lucee have mostly fixed those issues, and BL never had them If a value can be cast to the type, then it will work!
So, what does this mean? It means BIFs like
arrayAppend( arr, value )
structKeyExists( str, key )
queryEach( qry, ()->{} )
uCase( name )
go away. They no longer exist. Gone from the language. Goodbye!
And instead, these now exist only in member form! (Just like most other language in existence)
arr.append( value )
str.keyExists( key )
qry.each( ()->{} )
name.uCase()
Nice, tidy, and object-oriented around the native types of our language. We’d still have lots of BIFs-- heck CF has nearly 900 BIFs! This just applies to BIFs that operate on a type and have a matching member method. Also note, CF WOULD REMAIN UNCHANGED AND WILL STILL HAVE ALL THE BIFS! Thanks to the magic of our separate CF parser and transpiler, we’d still parse CF code like always, but behind the scenes, we’d just transpile any of these BIFs over to use the member method syntax. And tools like our CF → BL CLI Transpiler would automatically refactor your code for you. So you really wouldn’t have to lift a finger to make this change other than remember to type the member version of each function when writing BL code from scratch in the future. Something the coding hints in our VSCode extension can help with.
Ok, so what BIFs ARE we talking about? We’ll, I built this list automatically from the metadata inside of BoxLang by pulling every BIF (and BIF alias) that also have matching member methods. I excluded the following BIFs because even though they have member versions, they aren’t a one-to-one mapping and don’t quite make sense:
hash()
fileInfo()
dump()
createODBCDateTime()
What’s left is 280 BIFs we’d be effectively removing from BoxLang source code as reserved headless function names. There may be a couple you don’t recognize, such as getTime()
which is new to BoxLang. You can find info for it in our docs.
So the questions are
- Does this all make sense and like a good direction?
- Have I missed any?
- What perhaps DOESN’T belong in this list? Some of the number BIFs, for example, don’t have as strong of a precedent. The
exp( myNum )
BIF for instance-- in java, you callMath.exp( myNum )
as a static method-- it’s NOT a member method on the actual number likemyNum.exp()
. So do ALL of these make sense, or should we keep some of these around as BIF or look at concepts like a staticMath
class for some of them? Or BIFs likeJSONDeserialize()
which I never really loved the member form ofsomeString.JSONDeserailize()
. That one still feels like perhaps it should remain a headless utility function. - Have we forgotten any compat issues this may cause?
Here is the full list for you to review. The right two columns are more of an implementation detail-- they track the argument position that the data type gets passed into the BIF when calling as a member method. Usually the first arg, but not always.
BIF Name | Member Name | Argument Name | Argument Position |
---|---|---|---|
arrayappend() | array.append() | array | 1 |
arrayavg() | array.avg() | array | 1 |
arrayclear() | array.clear() | array | 1 |
arraydelete() | array.delete() | array | 1 |
arraydeletenocase() | array.deletenocase() | array | 1 |
arraydeleteat() | array.deleteat() | array | 1 |
arrayeach() | array.each() | array | 1 |
arrayevery() | array.every() | array | 1 |
arrayfilter() | array.filter() | array | 1 |
arrayfind() | array.find() | array | 1 |
arrayfindnocase() | array.findnocase() | array | 1 |
arraycontains() | array.contains() | array | 1 |
arraycontainsnocase() | array.containsnocase() | array | 1 |
arrayfindall() | array.findall() | array | 1 |
arrayfindallnocase() | array.findallnocase() | array | 1 |
arrayfirst() | array.first() | array | 1 |
arraygetmetadata() | array.getmetadata() | array | 1 |
arrayindexexists() | array.indexexists() | array | 1 |
arrayisdefined() | array.isdefined() | array | 1 |
arrayinsertat() | array.insertat() | array | 1 |
arraylast() | array.last() | array | 1 |
arraymap() | array.map() | array | 1 |
arraymax() | array.max() | array | 1 |
arraymedian() | array.median() | array | 1 |
arraymerge() | array.merge() | array1 | 1 |
arraymin() | array.min() | array | 1 |
arraypop() | array.pop() | array | 1 |
arrayprepend() | array.prepend() | array | 1 |
arraypush() | array.push() | array | 1 |
arrayrange() | array.range() | from | 1 |
arrayreduce() | array.reduce() | array | 1 |
arrayreduceright() | array.reduceright() | array | 1 |
arrayresize() | array.resize() | array | 1 |
arrayreverse() | array.reverse() | array | 1 |
arrayset() | array.set() | array | 1 |
arrayshift() | array.shift() | array | 1 |
arrayslice() | array.slice() | array | 1 |
arraymid() | array.mid() | array | 1 |
arraysome() | array.some() | array | 1 |
arraysort() | array.sort() | array | 1 |
arraysplice() | array.splice() | array | 1 |
arraysum() | array.sum() | array | 1 |
arrayswap() | array.swap() | array | 1 |
arraytolist() | array.tolist() | array | 1 |
arraytostruct() | array.tostruct() | array | 1 |
arrayunshift() | array.unshift() | array | 1 |
jsondeserialize() | string.jsondeserialize() | json | 1 |
jsonprettify() | string.jsonprettify() | var | 1 |
jsonserialize() | custom.tojson() | var | 1 |
tobase64() | string.tobase64() | string_or_object | 1 |
tobinary() | string.tobinary() | base64_or_object | 1 |
toimmutable() | array.toimmutable() | value | 1 |
tomutable() | array.tomutable() | value | 1 |
tostring() | xml.tostring() | value | 1 |
isempty() | string.isempty() | value | 1 |
isempty() | query.isempty() | value | 1 |
structisempty() | struct.isempty() | value | 1 |
arrayisempty() | array.isempty() | array | 1 |
hmac() | string.hmac() | input | 1 |
booleanformat() | numeric.booleanformat() | value | 1 |
decimalformat() | numeric.decimalformat() | number | 1 |
numberformat() | numeric.numberformat() | number | 1 |
lsnumberformat() | numeric.lsnumberformat() | number | 1 |
currencyformat() | numeric.currencyformat() | number | 1 |
lscurrencyformat() | numeric.lscurrencyformat() | number | 1 |
filereadline() | file.readline() | file | 1 |
fileseek() | file.seek() | file | 1 |
fileskipbytes() | file.skipbytes() | file | 1 |
filesetaccessmode() | file.setaccessmode() | file | 1 |
filesetattribute() | file.setattribute() | file | 1 |
filesetlastmodified() | file.setlastmodified() | file | 1 |
filewriteline() | file.writeline() | file | 1 |
gettoken() | string.gettoken() | string | 1 |
listappend() | string.listappend() | list | 1 |
listavg() | string.listavg() | list | 1 |
listchangedelims() | string.listchangedelims() | list | 1 |
listcompact() | string.listcompact() | list | 1 |
listtrim() | string.listtrim() | list | 1 |
listdeleteat() | string.listdeleteat() | list | 1 |
listeach() | string.listeach() | list | 1 |
listevery() | string.listevery() | list | 1 |
listfilter() | string.listfilter() | list | 1 |
listfind() | string.listfind() | list | 1 |
listfindnocase() | string.listfindnocase() | list | 1 |
listcontains() | string.listcontains() | list | 1 |
listcontainsnocase() | string.listcontainsnocase() | list | 1 |
listgetat() | string.listgetat() | list | 1 |
listfirst() | string.listfirst() | list | 1 |
listlast() | string.listlast() | list | 1 |
listindexexists() | string.listindexexists() | list | 1 |
listinsertat() | string.listinsertat() | list | 1 |
listitemtrim() | string.listitemtrim() | list | 1 |
listlen() | string.listlen() | list | 1 |
listmap() | string.listmap() | list | 1 |
listprepend() | string.listprepend() | list | 1 |
listqualify() | string.listqualify() | list | 1 |
listreduceright() | string.listreduceright() | list | 1 |
listremoveduplicates() | string.listremoveduplicates() | list | 1 |
listrest() | string.listrest() | list | 1 |
listsetat() | string.listsetat() | list | 1 |
listsome() | string.listsome() | list | 1 |
listsort() | string.listsort() | list | 1 |
listtoarray() | string.listtoarray() | list | 1 |
listvaluecount() | string.listvaluecount() | list | 1 |
listvaluecountnocase() | string.listvaluecountnocase() | list | 1 |
abs() | numeric.abs() | value | 1 |
acos() | numeric.acos() | number | 1 |
asin() | numeric.asin() | number | 1 |
atn() | numeric.atn() | number | 1 |
ceiling() | numeric.ceiling() | number | 1 |
cos() | numeric.cos() | number | 1 |
decrementvalue() | numeric.decrementvalue() | number | 1 |
exp() | numeric.exp() | number | 1 |
fix() | numeric.fix() | number | 1 |
floor() | numeric.floor() | number | 1 |
formatbasen() | numeric.formatbasen() | number | 1 |
incrementvalue() | numeric.incrementvalue() | number | 1 |
inputbasen() | string.inputbasen() | string | 1 |
int() | numeric.int() | number | 1 |
log() | numeric.log() | number | 1 |
log10() | numeric.log10() | number | 1 |
round() | numeric.round() | number | 1 |
sgn() | numeric.sgn() | number | 1 |
sin() | numeric.sin() | number | 1 |
sqr() | numeric.sqr() | value | 1 |
tan() | numeric.tan() | number | 1 |
queryaddcolumn() | query.addcolumn() | query | 1 |
queryaddrow() | query.addrow() | query | 1 |
queryappend() | query.append() | query1 | 1 |
queryclear() | query.clear() | query | 1 |
querycolumnarray() | query.columnarray() | query | 1 |
querycolumncount() | query.columncount() | query | 1 |
querycolumndata() | query.columndata() | query | 1 |
querycolumnexists() | query.columnexists() | query | 1 |
querycurrentrow() | query.currentrow() | query | 1 |
querydeletecolumn() | query.deletecolumn() | query | 1 |
querydeleterow() | query.deleterow() | query | 1 |
queryeach() | query.each() | query | 1 |
queryevery() | query.every() | query | 1 |
queryfilter() | query.filter() | query | 1 |
querygetcell() | query.getcell() | query | 1 |
querygetresult() | query.getresult() | query | 1 |
queryinsertat() | query.insertat() | query | 1 |
querykeyexists() | query.keyexists() | query | 1 |
querymap() | query.map() | query | 1 |
queryprepend() | query.prepend() | query1 | 1 |
queryrecordcount() | query.recordcount() | query | 1 |
queryreduce() | query.reduce() | query | 1 |
queryreverse() | query.reverse() | query | 1 |
queryrowdata() | query.rowdata() | query | 1 |
queryrowswap() | query.rowswap() | query | 1 |
querysetcell() | query.setcell() | query | 1 |
querysetrow() | query.setrow() | query | 1 |
queryslice() | query.slice() | query | 1 |
querysome() | query.some() | query | 1 |
querysort() | query.sort() | query | 1 |
ascii() | string.ascii() | string | 1 |
camelcase() | string.camelcase() | string | 1 |
charsetdecode() | string.charsetdecode() | encoded_binary | 1 |
compare() | string.compare() | string1 | 1 |
comparenocase() | string.comparenocase() | string1 | 1 |
find() | string.find() | string | 2 |
findnocase() | string.findnocase() | string | 2 |
findoneof() | string.findoneof() | string | 2 |
insert() | string.insert() | string | 2 |
ljustify() | string.ljustify() | string | 1 |
rjustify() | string.rjustify() | string | 1 |
kebabcase() | string.kebabcase() | string | 1 |
lcase() | string.lcase() | string | 1 |
left() | string.left() | string | 1 |
listreduce() | string.listreduce() | list | 1 |
ltrim() | string.ltrim() | string | 1 |
mid() | string.mid() | string | 1 |
paragraphformat() | string.paragraphformat() | string | 1 |
pascalcase() | string.pascalcase() | string | 1 |
refind() | string.refind() | string | 2 |
refindnocase() | string.refindnocase() | string | 2 |
rematch() | string.rematch() | string | 2 |
rematchnocase() | string.rematchnocase() | string | 2 |
removechars() | string.removechars() | string | 1 |
replace() | string.replace() | string | 1 |
replacelist() | string.replacelist() | string | 1 |
replacelistnocase() | string.replacelistnocase() | string | 1 |
replacenocase() | string.replacenocase() | string | 1 |
rereplace() | string.rereplace() | string | 1 |
rereplacenocase() | string.rereplacenocase() | string | 1 |
reverse() | string.reverse() | string | 1 |
right() | string.right() | string | 1 |
rtrim() | string.rtrim() | string | 1 |
slugify() | string.slugify() | string | 1 |
snakecase() | string.snakecase() | string | 1 |
spanexcluding() | string.spanexcluding() | string | 1 |
spanincluding() | string.spanincluding() | string | 1 |
sqlprettify() | string.sqlprettify() | sql | 1 |
stringbind() | string.bind() | string | 1 |
stringreduceright() | string.stringreduceright() | list | 1 |
stringsome() | string.stringsome() | list | 1 |
stringsort() | string.stringsort() | list | 1 |
stripcr() | string.stripcr() | string | 1 |
trim() | string.trim() | string | 1 |
truefalseformat() | string.truefalseformat() | value | 1 |
ucase() | string.ucase() | string | 1 |
ucfirst() | string.ucfirst() | string | 1 |
val() | string.val() | string | 1 |
wrap() | string.wrap() | string | 1 |
yesnoformat() | string.yesnoformat() | value | 1 |
structappend() | struct.append() | struct1 | 1 |
structclear() | struct.clear() | structure | 1 |
structcopy() | struct.copy() | struct | 1 |
structdelete() | struct.delete() | struct | 1 |
structeach() | struct.each() | struct | 1 |
structequals() | struct.equals() | struct1 | 1 |
structevery() | struct.every() | struct | 1 |
structfilter() | struct.filter() | struct | 1 |
structfind() | struct.find() | struct | 1 |
structfindkey() | struct.findkey() | struct | 1 |
structfindvalue() | struct.findvalue() | struct | 1 |
structget() | struct.getfrompath() | object | 2 |
structgetmetadata() | struct.getmetadata() | struct | 1 |
structinsert() | struct.insert() | struct | 1 |
structiscasesensitive() | struct.iscasesensitive() | struct | 1 |
structisordered() | struct.isordered() | struct | 1 |
structkeyarray() | struct.keyarray() | structure | 1 |
structkeyexists() | struct.keyexists() | struct | 1 |
structkeylist() | struct.keylist() | structure | 1 |
structkeytranslate() | struct.keytranslate() | struct | 1 |
structmap() | struct.map() | struct | 1 |
structreduce() | struct.reduce() | struct | 1 |
structsome() | struct.some() | struct | 1 |
structsort() | struct.sort() | struct | 1 |
structtoquerystring() | struct.toquerystring() | struct | 1 |
structtosorted() | struct.tosorted() | struct | 1 |
structupdate() | struct.update() | struct | 1 |
structvaluearray() | struct.valuearray() | struct | 1 |
urlencodedformat() | string.urlencodedformat() | string | 1 |
dateadd() | datetime.add() | date | 3 |
datecompare() | datetime.compare() | date1 | 1 |
datediff() | datetime.diff() | date1 | 2 |
datetimeformat() | datetime.format() | date | 1 |
dateformat() | datetime.dateformat() | date | 1 |
timeformat() | datetime.timeformat() | date | 1 |
lsdatetimeformat() | datetime.lsdatetimeformat() | date | 1 |
lsdateformat() | datetime.lsdateformat() | date | 1 |
lstimeformat() | datetime.lstimeformat() | date | 1 |
lsparsedatetime() | string.lsparsedatetime() | date | 1 |
lsweek() | datetime.lsweek() | date | 1 |
lsdayofweek() | datetime.lsdayofweek() | date | 1 |
parsedatetime() | string.parsedatetime() | date | 1 |
year() | datetime.year() | date | 1 |
quarter() | datetime.quarter() | date | 1 |
month() | datetime.month() | date | 1 |
monthasstring() | datetime.monthasstring() | date | 1 |
monthshortasstring() | datetime.monthshortasstring() | date | 1 |
day() | datetime.day() | date | 1 |
dayofweek() | datetime.dayofweek() | date | 1 |
dayofweekasstring() | datetime.dayofweekasstring() | date | 1 |
dayofweekshortasstring() | datetime.dayofweekshortasstring() | date | 1 |
daysinmonth() | datetime.daysinmonth() | date | 1 |
daysinyear() | datetime.daysinyear() | date | 1 |
dayofyear() | datetime.dayofyear() | date | 1 |
firstdayofmonth() | datetime.firstdayofmonth() | date | 1 |
weekofyear() | datetime.weekofyear() | date | 1 |
hour() | datetime.hour() | date | 1 |
minute() | datetime.minute() | date | 1 |
second() | datetime.second() | date | 1 |
millisecond() | datetime.millisecond() | date | 1 |
nanosecond() | datetime.nanosecond() | date | 1 |
offset() | datetime.offset() | date | 1 |
gettimezone() | datetime.timezone() | date | 1 |
getnumericdate() | datetime.getnumericdate() | date | 1 |
gettime() | datetime.gettime() | date | 1 |
len() | struct.len() | value | 1 |
structcount() | struct.count() | value | 1 |
arraylen() | array.len() | value | 1 |
stringlen() | string.len() | value | 1 |
xmlchildpos() | xml.childpos() | elem | 1 |
xmlformat() | string.xmlformat() | string | 1 |
xmlgetnodetype() | xml.getnodetype() | XMLNode | 1 |
xmlsearch() | xml.search() | XMLNode | 1 |
xmltransform() | xml.transform() | XML | 1 |