A "good function"

Antranig Basman antranig.basman at colorado.edu
Thu Jan 8 15:45:58 UTC 2015


A "good function" is

i) A pure function, one without side-effects whose return value depends only on its arguments
ii) One which is available at a stable public name in a global namespace
iii) One which performs work only linearly proportional to the size of its arguments (considered as structures)


The first criterion has long been of recognised importance to Computer Science - 
https://en.wikipedia.org/wiki/Pure_function . The second criterion has been increasingly recognised by us in 
Fluid, especially in our more mature work after, say, mid-2009. The third criterion is only more recently 
becoming recognised in its value an implications.

The prime goal of all our work is transparency - primarily, transparency of the systems that we create from 
the point of view of our integrators, users, and the tools that they use (including any UI of the resulting 
system itself) - without transparency, we can't deliver on accessibility, which requires all the behaviour 
of the system to be at the user's disposal. Criterion iii) is specifically a reaction against the very 
popular notion of a "computationally complete" or "Turing-complete" language or system. By definition, such 
a system can "compute any computable function" and therefore also by definition one that is as least 
transparent as possible. A system which can "compute any computable function" could indeed do absolutely 
anything - including consuming unbounded resources of time and space, as well as possibly never terminating. 
These are "classically undesirable" features of any user system - but together with these undesirable 
properties come other, analogous problems of the kind we are more interested in in our communities: a system 
based on this kind of code is hard to analyse, hard to transform and adapt for other purposes, and hard to 
author by non-specialists. I think it's only more recently becoming clear that attacking such problems from 
the angle of strict bounds on complexity (and thus on coding styles) is a productive way of finding our way 
to meeting these more precarious and indefinable values through relatively tractable and hard-edged, 
measurable properties.

This issue came up since I was recently talking over with Colin a "transforming function" I had written for 
our OAuth-secured flow manager's test fixtures, named "gpii.test.cloudBased.oauth2.buildDisruptedFixture" 
and currently housed at 
https://github.com/simonbates/universal/blob/GPII-17/gpii/node_modules/testing/src/CloudBasedOAuth2.js#L343 
in Simon's branch of GPII universal.

There are a few things about this function which are rather clunky and undesirable - although it does at 
least embody a model that "Infusion IoC is a JSON dialect which has JavaScript as its metalanguage", 
implying that we at least *have* a metalanguage, and it is a commonly-understood and widely used one - as 
opposed to other environments which either have no metalanguage or have an ad hoc one written in an 
unintelligible dialect such as "aspects". Eliminating those kinds of systems leaves only LISP in the same 
camp as us, which astonishingly succeeded in being a language which almost precisely coincided with its own 
metalanguage.

Why do we need a metalanguage of this or any kind? It is because this is the language that our authoring 
tools will one day be written in. But for the time being, if we left the issue by saying that "our 
metalanguage is JavaScript", it could be argued that we've made our problem substantially worse - 
JavaScript, being Turing-complete is itself very hard to author effectively and transparently. So I was left 
pondering, "What is that, if anything, that is actually 'good' about functions such as 
'gpii.test.cloudBased.oauth2.buildDisruptedFixture'", and could initially only come up with points i) and 
ii) above which didn't seem terribly promising.

However, putting condition iii) into the mix does seem to leave us somewhat further along. For a start, 
there are vastly fewer functions that satisfy condition iii), and there are also vastly fewer language 
features and coding styles which are permissible in them. They are able to use all the basic operators of 
JavaScript (member access, arithmetic/logic operations, etc.) as well as one of the control flow elements - 
if/then/else. However, they must obey strong restrictions in order to be "obviously provable" from a static 
analysis that they meet condition iii) -

a) Any looping primitives must be "structural" - that is, take the form of a fluid.each or fluid.transform 
call applied to their direct arguments in order to be obviously "non-work-magnifying" - that is, not doing 
work which grows faster than the size of their arguments - this is referred to as the use of "structural 
recursion" https://en.wikipedia.org/wiki/Recursion_(computer_science)#Structural_versus_generative_recursion 
(as opposed to "generative recursion")
b) Any functions that they call must also obey the same restrictions i) through iii)

It is sort of promising that the space of these "good functions" appear to be algebraically closed in this 
way - that is, a "good function" which obeys these stylistic restrictions and only calls other "good 
functions" is itself provably "good" - this suggests that we are identifying something potentially stable 
and meaningful.

Notes:

A) Note that the "cost criterion" in iii) could be relaxed slightly (or sometimes) to permit functions of 
O(n log n) in input size, which would gain us access to elementary sorting and searching algorithms. But the 
original form of the criterion is the one which could most straightforwardly be checked by authoring tools, 
allowing the location of "good" and "non-good" functions of various levels of quality to be immediately 
identified.

B) Note that criterion i) especially in JavaScript should be clarified to read "and one that does not modify 
the structures passed as its arguments"

c) None of this discussion directly touches the issue of how we *do* handle mutable state when we meet it, 
which is a separate though linked discussion - the link being our model transformation system -

Model transformations:

The classic embodiment of "good functions" are the transform functions attached to our model transformation 
framework documented at http://wiki.gpii.net/index.php/Architecture_-_Available_transformation_functions - 
these functions are all classically "good" but unfortunately our system is still far too primitive to allow 
functions like "gpii.test.cloudBased.oauth2.buildDisruptedFixture" to be encoded in it - as well as the 
resulting structure being quite verbose and hard to read/author with our current encoding. Eventually our 
transforms should be powerful and usable enough to be the "expression of choice" for essentially all "good 
functions" such as this one. However, it is helpful to identify the way in which this function and others 
might be "on the way to becoming good" ... as well as starting to shrink the language space in which our 
"metalanguage" is to be expressed.

Cheers,
Antranig




More information about the fluid-work mailing list