%macro birthdateFromPnr(pnr); /* 1 */
%sysfunc(inputn(%substr(&pnr, 1, 8), yymmdd8.)) /* 2 */
%mend;
%put NOTE: Macro version: %birthdateFromPnr(19121212-1212); /* 3 */
In order to really make the function reusable, we should either save the source code and use the Autocall Facility or compile the macro and use the Stored Compiled Macro Facility.
FCMP function
Now let's implement the same logic in a function created by PROC FCMP.
proc fcmp outlib=work.functions.identity; /* 1 */
function birthdateFromPnr(pnr $); /* 2 */
return (input(substr(pnr, 1, 8), yymmdd8.)); /* 3 */
endfunc; /* 4 */
run;
options cmplib=work.functions; /* 5 */
data _null_;
birthdate = birthdateFromPnr('191212121212'); /* 5 */
putlog 'NOTE: FCMP version: ' birthdate;
run;
Just like with the macro function, we should of course share the function with our colleagues. Simply store the function in a dataset in a permanent library that they have access to, instead of saving it in WORK.
CASL function
Last but certainly not least, let's create a CASL function!
cas; /* 1 */
proc cas; /* 2 */
function birthdateFromPnr(pnr); /* 3 */
return (inputn(substr(pnr, 1, 8), 'yymmdd8.')); /* 4 */
end; /* 5 */
print (NOTE) 'CASL version: ' birthdateFromPnr('19121212-1212'); /* 6 */
run;
quit;
Just like with the other two function types, we wouldn't be our generous selves if we didn't share the function with our colleagues! Use the CASLstore functions for this.
When to use what function type?
Now that we know how to create these types of functions, the question is in what scenario should/could we use what function type? The short answer is:
What makes the longer answer more interesting is that there are techniques we can use to:
to name a few. So, it also comes down to personal preference.
One thing that we can only do with our CASL functions and not our FCMP functions is to use CAS Actions. Below is an example, including how we can store the function for later use.
cas;
proc cas;
source myFunction; /* 1 */
function nObs(dsIn); /* 2 */
simple.numrows r=nr / table=dsIn; /* 3 */
return nr.numrows; /* 4 */
end func; /* 5 */
endsource; /* 6 */
upload_caslstore({caslib='casuser', name='functions', replace=true}, myFunction); /* 7 */
table.promote / caslib='casuser', name='functions'; /* 8 */
run;
quit;
We can then use the function in other CAS sessions:
proc cas;
caslstore({caslib='casuser', name='functions'}); /* 1 */
print (NOTE) 'Table myTable in CASLIB myCaslib has ' nObs({caslib='myCaslib', name='myTable'}) 'observations!'; /* 2 */
run;
quit;
If we stored it in a CASLIB accessible by our colleagues, they could also use it!
Conclusion
Creating custom functions and sharing them with colleagues is easy and can be very useful. Different types of functions serve different purposes, and the application areas are infinite. Of course, functions can be shared with others than just your colleagues! Check out @yabwon's GitHub repository for the SAS Packages framework for some inspiration.
Short note about macro functions.
If you are interested in quick creation of bunch of "macro functions" that use %sysfunc "wrapping" you could look at the %generateOneLiners() macro, it's component of the basePlus package. You can run it like:
%GenerateOneLiners(prefix=my_
, listOfFunctions=DATETIME TODAY YEAR QTR MONTH WEEK DAY HOUR MINUTE SECOND
)
and you can enjoy convenient macro functions like:
%put %my_YEAR(%my_TODAY());
%put %my_MONTH(%my_TODAY());
There are some other useful functions in the basePlus too.
And if you are interested in FCMP functions mimicking data struuctures like FIFO queue, LIFO queue, etc. check out the DFA package (documentation)
All the best
Bart