Use of Smart Public Functions
Table of Contents
1 Abstract
Using smart public functions a devloper designates local and public functions. A public function may be accessed from any directory on the user PATH, a local function is accessed via a relative path.
This paper introduces the use of the SmartPublic practice. This idea supports library development on the local level, with a few functions enabled for public use. By this, I mean when you develop functons for a particular task, with experience, you recognize some which can be used more widely.
The smart function practice originated as "smart files". My practice has evoloved to use functions which return names of other objects: files, directories and functions. I call the practice "smart" because a smart functions's default behavior returns names and optional arguments perform other actions on the names. Examples are included
A first 'for instance' is the functions function:
functions () { : date: 2017-05-29; : date: 2017-08-10; awk '$2 ~ /^[(][)]$/ && !printed[$1]++ { print $1 }' ${*:--} }
This function began life in proflib, it's now found its way to my publiclib
$ whf functions /Users/applemcg/Dropbox/bin/proflib /Users/applemcg/Dropbox/bin/publiclib
2 Smart Public
First the smart functions, the public family relies on them, more than vice-vesa.
2.1 smart tag
This is the third itertion of the smart function family. See the 4 lesson for its evolution. While the smart family relies on lessons learned in defining families, it does not use any of the functions in the fam family. See the SHELF functions, which intoduces families, and uses the concept with four families in a single library.
The first requirement of a smart function is it's first argument must also be a function or a command. It must fulfill this bit of shell syntax:
smart_fun () { ${@:-echo} namea nameb ... ; } # such that,... declare -f $(smart_fun) # shows the function bodies, or smart_fun declare -f # also works, so the default smart_fun # simply lists their names.
The first functions in the smart family:
smart_def () { : date: 2017-06-10; : date: 2017-07-15; : date: 2017-08-05; : callStack; : date: 2018-01-07; trace_call $*; report_notargcount 2 $# listName member ... && return 1; local boiler=": mfg: $(myname 2);: date: $(date +%Y-%m-%d)"; eval "$1 () { $boiler; \${@:-echo} $(args_uniq ${*:2}); }" } smart_add () { : date: 2017-06-10; : date: 2017-07-15; : date: 2017-08-05; trace_call $*; smart_def $1 ${*:2} $( isfunction $1 && $1 ) } smart_del () { : date: 2017-06-10; : date: 2017-07-15; report_notfunction $1 && return 1; trace_call $*; smart_def $1 $(smart_trim $($1) ${*:2} $($1)) } smart_trim () { : see test case -- actual members are there twice,; : items to remove are present one more time,; : items NOT in the list are there once,; : .. an attempt to remove a non-member; : properties arranged by ARGS in smart_trim; : date: 2017-06-13; : todo: xlate to single awk script, save 4 processes.; echo $* | wpl | sort | uniq -c | awk '$1 == 2 { printf "%s ", $2 }' }
The smart_def and smart_del verify their arguments, then trace their calling arguments. Breaking one of my rules, smart_def uses a local variable to hold a long-ish string for the resulting function body. This string supplies a function tag, "mfg" signifying the function has been manufactured, the name of the function who called the smart function, and the date tag.
Here is the simplest possible demonstration. The "xx" function
uses smart_def to define the foo function. Notice the
mfg
and date
tags. The default behavior returns the value.
Used as a smart function, foo cat
returns the error.
$ xx () { smart_def foo bar; }; ff foo; foo; foo cat foo () { : mfg: xx; : date: 2017-10-26; ${@:-echo} bar } bar cat: bar: No such file or directory $
The lesson learned since the prior iteration is to trim the set of list-manipulation functions. The features are better addressed at a higher level.
And since we are not yet ready to be in the family way, a handful of functions which might be generated from standard templates are instead written by hand, copying existing example. In the chicken-or-egg puzzle, one thing I've learned is that some families live together. This means they may be tested together. Thus more then one family may occupy a function library.
Here are the template-like functions in the smart family:
$ ff smart_{public,init,source} smart_public () { : mfg: smart_init; : date: 2017-10-26; ${@:-echo} smart_functions smart_init smart_install smart_source smart_value smart_list } smart_init () { smart_def smart_source $PWD; smart_def smart_public smart_{functions,init,source} } smart_source () { : mfg: smart_init : date: 2017-10-26; ${@:-echo} /Users/applemcg/Dropbox/commonplace/textindex }
Note that smart_public is itself a smart_def, instantiated in the smart_init. Also, smart_source is a smart_def, also manufactured.
- smart_source
This later, smart_source is worth a moment of reflection. In a peek ahead, the function public_function_list stores it's arguments, in this case the names of the smart_function, in the current public function library. This function's name might just as well have been "to the public library". Also, a bit perverse, and in this case just a little too obtuse, but
smart_functions public_function_library # works, suggesting instead smart_functions to_the_public_library # would also work!
The question arises, if both smart_functions and smart_source are manufactured, why take notice of them at all, since they will be recreated anytime the library in invokded, or "sourced".
The answer lies in the question: "what does the _source function do for us?" It tells us where the source code for this family of functions is found. So, those functions in the smart_functions list are installed in the public library.
So one of the uses of a _source function, such as smart_source is to remember where the source for the smart function is found:
$ pushd $(smart_source) # more appropriately, $ smart_source pushd # and consider $ smart_source ls -l
In order for smart_source to be installed, it must be in the smart_functions list. And here's the fun part, if the functions are collected, put in another local library, in another directory, and subsquently re-installed, the smart_source will reflect the new, current location.
2.2 public
Without public functions, it's not worth much to only be smart.
For the present, the PUBLIClib is publiclib, so, for example
$ public_source /Users/applemcg/Dropbox/inc
And since the function is a smart function
$ public_source ls -l total 80 -rw-r--r--@ 1 applemcg staff 1529 Oct 26 07:03 chlib -rw-r--r--@ 1 applemcg staff 1493 Oct 25 12:18 classes.def -rw-r--r--@ 1 applemcg staff 304 Oct 25 12:43 crutches.def -rw-r--r--@ 1 applemcg staff 307 Feb 7 2017 dzwrap.def -rwxr-xr-x@ 1 applemcg staff 4929 Oct 26 07:43 fixlib drwxr-xr-x@ 2 applemcg staff 68 Oct 26 07:44 lib -rw-r--r--@ 1 applemcg staff 3805 Oct 25 12:44 src2ht.def -rw-r--r--@ 1 applemcg staff 91 Oct 24 12:50 src2ht.src -rw-r--r--@ 2 applemcg staff 1848 Oct 18 10:30 tablefields.def -rw-r--r--@ 1 applemcg staff 1982 Oct 25 12:05 tagstack.def
Which works regardless of where the command was issued.
3 Implementation
The first requirement of a smart function is it's first argument must also be a function or a command. It must fulfill this bit of shell syntax:
smart_fun () { ${@:-echo} namea nameb ... ; } declare -f $(smart_fun) # shows the function bodies, or smart_fun declare -f # also works, so the default smart_fun # simply lists their names.
3.1 list maintenance
After quite a bit of re-work, the list maintenance functions are now
regular. They accept the name of the smart list followed by the
arguments, either to create, add names, or delete names. In order to
expose the deletion mechanism, the smart_trim function is also
include. Note, smart_add could also be used to create the initial
list. For safety's sake, a defensive programmer would unset
the
smart name. Also included is the smart_value function, which is a
list with a single item.
$ declare -f smart_list smart_add smart_del smart_trim smart_value smart_list () { : date: 2017-06-10; : date: 2017-06-14; report_notargcount 2 $# listName member ... && return 1; local boiler=": mfg: $(myname 2);: date: $(date +%Y-%m-%d)"; eval "$1 () { $boiler; \${@:-echo} $(args_uniq ${*:2}); }" } smart_add () { : date: 2017-06-10; : date: 2017-06-14; trace_call $*; smart_list $1 ${*:2} $( isfunction $1 && $1 ) } smart_del () { : date: 2017-06-10; : date: 2017-06-14; report_notfunction $1 && return 1; trace_call $*; smart_list $1 $(smart_trim $($1) ${*:2} $($1)) } smart_trim () { : see test case -- actual members are there twice,; : items to remove are present one more time,; : items NOT in the list are there once,; : .. an attempt to remove a non-member; : properties arranged by ARGS in smart_trim; : date: 2017-06-13; echo $* | wpl | sort | uniq -c | awk '$1 == 2 { printf "%s ", $2 }' } smart_value () { : values may have only one item in their list; : date: 2017-06-14; smart_list $1 $2 } $
The functions smart_list, smart_add, and smart_del respectively create the list, add list members, and trim, or delete mamebers from the list. This latter function uses smart_trim, which uses the smart list (twice), and the arguments to trim. The algorithm is: retain arguments appearing twice, since since an argument to trim will appear three times and an argument appearing once was an attempt to trim an item which is not in the list.
3.2 public functions
An important purpose of this project is to separate the public face of a function family from it's local components. The requirement arises from my Library Development practice. What's evolving is the following hierarchy:
- publiclib – where the public functions for all families belong
- locallib – where the local functions for a given family belongs
- fixlib – temporary. fixes to
locallib
orpubliclib
functions belong
Functions in fixlib may belong to the local locallib, or the public library. When local tests indicate a fixlib function is ready, it may be promoted to the local locallib, regardless or not if it was hosted in that locallib. Here's a typical command:
$ . fixlib $ libsave ./locallib $(flcomm -12 ./locallib fixlib) $ lib_crunch locallib $ ff $(flcomm -13 ./locallib fixlib) | tee .l $ backup_lib localib fixlib $ mv .l fixlib; backup_lib fixlib
The first flcomm
identifies the functions in common in locallib, fixlib;
the version in the fixlib is appended to the locallib. The lib_crunch
eliminates the older version of the duplicate functions.
The second flcomm
identifes the functions uniq to the fixlib; their
function bodies are saved in a temporary file, which after backups is
moved to remplace the existing fixlib, again backed up.
The process may be repeated for publiclib
and the locallib
3.3 local functions
It seems a necessary feature, after functions are moved to the publiclib
is to record where they came from. The idea is to define a function:
{family}_source
which displays the source directory where the development
has taken place. In order to define the function, use PWD:
$ fuse smart_locality smart_init smart_function smart_locality; $ ff smart_function smart_locality smart_source smart_function () { : date: 2017-06-14; isfunction $1 && $@ } smart_locality () { : date: 2017-06-14; smart_value smart_source $PWD } smart_source () { : mfg: smart_value; : date: 2017-06-14; ${@:-echo} /users/applemcg/Dropbox/commonplace/lit } $
In this example, fuse
shows where the function was used. So,
smart_locality is called, through smart_function during
intialization. If smart_source is part of smart_public_list, it
is added to the public library. By keeping smart_locality off the
public list, then it is not installed, and the value of the fixed
library is retained in the definition installed in the public library.
This needs a better explanation.
4 History
The smart list is a generalization of what I'd previously called a smart file. A smartfile is, in effect, a smart_list with a single element. It might be called a smart_value.
This practice is now obsolescent. Collecting the smartfiles in a central repository was more trouble than it was worth. These paragraphs will soon become COMMENTs in this document.
5 function references
- recorg
- fam_template_text
- FAM_init
- FAM_locality
- fam_template
- reNew_init
- reNew_locality
- smart_list
- smart_add
- smart_del
- smart_trim
- smart_value
- smart_function
- smart_locality
- smart_source
6 references
- this paper appears online at http://mcgowans.org/pubs/marty3/commonplace/software/smartpublic.html
- the Red Chapter
- library development
- SHELF proposal.
7 history
event | date | comment |
---|---|---|
opened | cloned from development library | |
first main edit | simplified smart, public | |