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.

  1. 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 or publiclib 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

7 history

event date comment
opened <2017-07-18 Tue> cloned from development library
first main edit <2017-10-26 Thu> simplified smart, public
     

Author: Marty McGowan

Created: 2018-07-10 Tue 16:50

Emacs 24.4.1 (Org mode 8.2.10)

Validate