Created
September 11, 2015 10:33
-
-
Save CinchBlue/8198908796090f3d5d75 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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