Skip to content

Instantly share code, notes, and snippets.

@CinchBlue
Created September 11, 2015 10:33
Show Gist options
  • Save CinchBlue/8198908796090f3d5d75 to your computer and use it in GitHub Desktop.
Save CinchBlue/8198908796090f3d5d75 to your computer and use it in GitHub Desktop.
There is a number N such that N represents the amount of ambiguity in a C++ function F.
The above statement is ambiguous. Let us consider the following C++ function F(x):
bool F(bool x) {
if (x) {
return true;
} else {
return false;
}
}
There is only 1 input to F: x. x has only 2 possible values that it can take on; F(x) can take on only two possible outputs.
For every input x, we can directly compute the result of F(x) with certainty.
However, let us consider the function G(x) with a global b;
bool b;
bool G(bool x) {
if (x && b) {
return true;
} else {
return false;
}
}
Let us define the scope of our analysis of ambiguity to be the scope of the C++ function G.
Let us define the input of a function F to the set of arguments to F as well as the set of variables that are referenced
within the function F that exist outside the scope of F that are not function arguments of F.
Let us define the explicit input of a function to be the set of arguments given to a function.
Let us define the implicit input of a function F to be the set of variables referenced from within the functin that exist outside of
scope of F;
There are 2 inputs to the function G: 1 implicit (b) and 1 explicit (x).
Since our scope of analysis is G, we cannot reason about the value of b.
Our truth table of possible inputs/outputs of G is such:
| x | b | G |
F F F
F T F
T F F
T T T
However, one we consider our scope of analysis, we cannot know the value of b.
Let us define a third logical truth value, ambiguity, A, such that it represents the superposition of T and F or the unresolved value.
Our new truth table of G with the scope of analysis of G is such:
| x | b | G |
F A F
F A F
T A A
T A A
Therefore, there are truly only two distinct cases:
| x | b | G |
F A F
T A A
Let us define the measure of ambiguity M of a function F with a scope of analysis S as the number of possible ambiguous outputs
of a function F over the total possible number of outputs from a function F given a scope of analysis S.
Therefore, as we have 1/2, we have 0.5 or 50% ambiguity caused by the ambiguity of b, the global variable.
Therefore, there is a number N such that N represents the amount of ambiguity in a C++ function F: it is M.
__________________________________________________________________________________________________________________________________
Next, consider the following function:
unsigned char div(unsigned char a, unsigned char b) {
return a/b;
}
We calculate the measure of ambiguity of div with the scope of div. It is 0.
However, there is at least one case in which we get undefined behavior: where b = 0.
Let us extend the definition of A to include the case where computing a function F with a set of given input I results
in undefined behavior.
Therefore, there is a minimum bound on the measure of ambiguity M; M >= 1 / (2^(8*sizeof(a)) * 2^(8*sizeof(b))): about 0.000015.
It matters more that there is an undefined, ambiguous case. But we know it is caused by b = 0.
To reduce ambiguity, simply make it so that b = 0 results in a defined cases that gives a distinct output.
Without using exceptions, let us define a very crude class template: Maybe:
template <class T>
struct Maybe{
Maybe(T data) : valid(true), data(data)
Maybe(bool b, T data) : valid(b), data(data) {}
bool valid;
T data;
};
template <class T>
Maybe<unsigned char> operator/(Maybe<T> lhs, Maybe<T> rhs) {
if (rhs != 0 && lhs.valid) {
returh {true, lhs.data / rhs.data};
} else {
return {false, lhs.data};
}
}
If we substitute all of our types with Maybe class templates:
Maybe<unsigned char> H(Maybe<unsigned char> a, Maybe<unsigned char> rhs) {
return lhs / rhs;
}
Since Maybe is a composite type, we have more possibilities--however, our M is given scope of H is now 0. Our ambiguous/undefined case
is gone. Our function can now be considered pure AND non-ambiguous.
QUESTIONS/CRITICISM:
- Why do we care about the measure of ambiguity? It makes no difference between 0.01 and 0.001 of M!
> We can start by defining a way to pinpoint ambiguous cases such that we define a more ambiguous-isolating domain/co-domain of a function F
to be such that M of our new domain/co-domain is higher than that of our last one. A domain becomes ambiguous if all of its input cases
result in ambiguous output or behavior.
- Why do we lump in undefined behavior with values left out of scope as both A?
> Both cannot be precisely reasoned about--although a function may be pure, the definition of pure does not account for any cases
in which computation of a function with given inputs may result in undefined behavior and an invalid state of the program.
Regardless, invalid states and undefined behavior are neither valid nor defined--we are looking for a way to measure the
ambiguity of a program and, therefore, how much we can successfully reason about a program without having to digest the entire
tree of logic of a program (which a regular human should be incapable of doing given a large enough program with enough statements
and expressions).
- Why is scope so important?
> Scope is just another way of isolating the domain of a program. If we use the largest universal scope, where all inputs, even impure,
are known, there IS no ambiguity in the program. However, bugs and unintended behavior of a program can be caused by ambigious behavior,
and all ambiguous behavior has an origin. The use of scope and narrowing scope is a way of isolating the origin of amiguity and, conversely,
isolating the lack of ambiguity within a certain section of a program. A section of a program that has no ambiguity should never crash,
given that all functions have their input/output cases accounted for.
- How is this useful?
> We can use M and A to begin to objectively compare designs such that function and program designs that reduce M are possibly
superior based on the grounds that reasoning with a program is more consistant given less ambiguity within its behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment