The Trivial Function
The Trivial Function:
F(∅)↠∅
The Trivial Function takes Nothing as its input, Does Nothing with it, and Returns a Null Value.
It's not very interesting.
Deterministic Functions
Deterministic Function:
F(x)=F(x)
For All x.
Or stated in English, given the same Input, a Deterministic Function will yield the same Output.
Deterministic Functions are Reliable, Predictable, and Mappable.
Mapping
Given
Function(Input)↠Output
.
If For All Valid Inputs (For All
i in Input) and All Outputs (All
o in Output), there exists a Set of Tuples of The Form {i
1:o
1, i
2:o
2, ...} such that F(i
x)=o
x, then such a Set of Tuples is Called a Mapping.
As stated, All Deterministic Functions are Mappable.
Well Formed Functions
Well Formed Function: Any Function which yields equivalent results if replaced with its Mapping.
In short, Well Formed Functions are Atomic and do not have Side-Effects.
A Well Formed Function does not alter or effect anything outside of itself.
Well Formed Functions do not change Global Values.
Monads
Monad: A Well Formed Function that Does Nothing and Returns its Input as its Output.
Thus, a Monad is a Pass Through Function. A True Monad (or Pure Monad) is indistinguishable from The Identity Function.
Monads are ONLY interesting (i.e. useful) when that Do Nothing Part is stretched to its limits to the point where
Nothing is Not Really Being Done; and rather, the something being done is Not Functionally Important or Mathematically Relevant to the task at hand.
Example Monad (that is nothing like a True Monad, but is a lot like The So-Called and/or Fake Monads used in Functional Programming): "Hold that thought," as I get myself a Cup of Hot Chocolate. "Now, where were we?"
Do not worry if you don't understand.
I promise not to mention Monads ever again... at least, not on this page.
Procedural Programming
Programming is little more than Stringing Functions Together. And the two simplest ways of Stringing Functions Together are either Sequentially or In Parallel.
For Well Formed Functions, both cases look pretty much the same.
def example_program():
step_one()
step_two()
step_three()
return "Success!"
If Run Sequentially,
step_one
Returns prior to
step_two
ever firing. Whereas, if Run In Parallel (also know as Asynchronous Execution) no such limitation exists and
step_three
may exit prior to
step_one
even beginning.
However, if either
step_one
,
step_two
, or
step_three
is Not Well Formed, The Execution Order can make a big difference.
Most Programs Run Sequentially. And most Programming Languages, assume Sequential Execution.
Certainly, if the above was Run in Python,
step_one
would exit prior to the start of
step_two
. But the addition of a few key-word
async
's here-and-there would change all that.
But as that's not really what this page is about, I'm going to skip all that and quietly move-on to a much more interesting way of Stringing Functions Together.
Composition: A First Try
↠F()↠ ↠G()↠ ↠H()↠
Wherein, The Output of F is used as The Input to G, whose Output in turn is used as The Input to H.
So it's easier to keep track of things down the line, let's make that a bit more verbose.
↠step_one()↠ ↠step_two()↠ ↠step_three()↠
And that is very similar to Command Line Pipes.
C:\>step_one | step_two | step_three
Which in turn (I believe), is very similar to Point Free Notation (namely,
F.G.H
or
step_one.step_two.step_three
). But don't quote me on that, as I could never get my Point Free Styled Commands to work with any degree of reliability back when I was experimenting with Haskell.
Rather, I often prefer a LISP style Notation, which uses endless Parentheses.
H(G(F()))
step_three(step_two(step_one)))
This may seem more complex at first, on account of the reverse ordering. But as the Parentheses Collapse in the exact same way that I learned back in Second Grade (or whenever that was), I am much more used to manipulating these sorts of constructions.
If the previous
example_program
is restructured, using a more LISP-like syntax with a few more parentheses (but the same key-words) the result is as follows:
def("example_program",(
step_one(),
step_two(),
step_three(),
return("Success!")
)
)
I know those few changes don't provide much clarity. So, let me highlight two points:
- All Functions Have The Same Form
function(first_argument, second_argument, ...)
- The Execution Order Is Explicit
- Just following the collapsing parentheses, innermost to outermost.
Was that useful?
Probably not.
You know what, I'm just going to kill the key-words (all of them) and reduce the program to a set of parentheses.
( (
(),
(),
(),
()
) )
And since White Space (Returns and Blank Spaces) are ignored in LISP, the foregoing can be reduced to a straightforward string of parentheses.
(((),(),(),()))
.
And if not to you, than at least to me, that last packs a lot of information. We are talking about an Outer Function that is dependent upon an Inner Function, which in turn is comprised of Four Discrete Steps.
Of course, some of you might be saying, "Duh! I knew that the second I saw The Python Code."
So, maybe, I should just say that if only Well Formed Functions are used, any number of Inner Functions (and thus, Inner Parentheses) may be removed and replaced by a key-word, in this case
example_program
example_program = () = (((),(),(),()))
Where & When The Parentheses are placed is just matter of Where & When Additional Detail is Desired.
Composition: Just The Facts
If
Function F
has
Input(F)
of
Type A
and
Output(F)
of
Type B
and
Function G
has
Input(G)
of
Type B
and
Output(G)
of
Type C
, then there ALWAYS exists a
Function H
which has
Input(H)
of
Type A
and
Output(H)
of
Type C
such that
G(F(A))=H(A)
. And the process of combining F & G is called Composition.
If
F(A)↠B
and
G(B)↠C
, then there exists a Composition
H(A)↠C
, such that
H(A)=G(F(A))
.
Math got you down?
Are Endless Parentheses slowly sapping your Will to Live? Or at least, causing Serious Annoyance and Eye Strain?
Then rejoice, for all that really matters is The Inter-Connect-Ability of Functions.
As long as The Types Match, New Functions can be built-up from Smaller Functions and Existing Functions can be broken-down into Smaller Functions
ad infinitum.
In short,
↠F(x)↠
is Arbitrarily Divisible and Expandable.
Types
Maybe, I should talk about Types for a second... but just a second.
A Type is another way of saying A Class of Objects.
A Type is The Name given to a Grouping of Nouns.
Type(Chicago) = Class City
Type(Banana) = Class Fruit
Type(Human) = Class Sentient
Of course, I could go down that list and give each Object (herein, Chicago, Banana, Human) a different Class, as Class is just an arbitrary way of Slicing & Dicing Reality.
Type(Chicago) = Class Crime Center
Type(Banana) = Class Snack Food
Type(Human) = Class Primate
Types & Classes are very much subjectively decided and could well be the subject of a whole other Rant, you know, if I cared about the subject that much.
What's relevant at the moment is that for two Functions to be Composible, The Output Type Class for The First Function must match The Input Type Class for The Second Function.
And having clarified that point, I will move on, never to mention it again.
Functional Programming
Functional Programming: A Programming Style which Exclusively Uses Only Well Formed Functions.
Sample Program Expansion
main()
main(step_one(),
step_two())
main(step_one(A(),B()),
step_two(A(B)))
Since I am not working with a Real World Problem, The Expansion is Arbitrary.
In the above, if
A
&
B
have Side-Effects (are Not Well Formed Functions), then there may be unintended Cross Over between
step_one
and
step_two
and the firing order can matter immensely.
However, if
A
&
B
are Well Formed, there's insulation between the steps, the wires never cross, and the parentheses do their job. This makes reasoning about The Program much (much-much) easier, as
A
only effects
A
and
step_one
only effects
step_one
.
If there are no Side-Effects, Effects are limited to The Local Scope.
- Reasoning Is Easier!
- Debugging Is Easier!
- And Programs Can Be Proved!
I really don't care about the latter. But the first two essentially mean The Act of Programming is Easier.
Game! Set! Match!
On Reasoning
This is a program.
((()())(((()()(()))()()((()))(()()()))(()())))
This is the same program.
(
(()())
(
((()()(()))
()
()
((()))
(()()())
)
(()())
)
)
For me, there's information to be discovered in The Placement of The Parentheses.
It's like a game of Connect The Dots, trying to see Patterns & Groupings.
I believe I became a Better Programmer once I started to Deconstruct Problems in this way.
Of course, most problems can be handled quite well by a Linear Script.
(()()()()())
And/Or:
(
()
()
()
()
()
)
So, there's no need to look for complexity, where it need not be.
Philosophy
Being who I am, just about as soon as I started to Program, I started Applying Programming Concepts to Philosophy.
RUN LIFE(SOUL)
- What can this tell us?
- How can we expand this?
- What is the Input?
- What is the Output?
Obviously, I don't have The Answers. But I do have a new way of Asking My Questions?
And I find the activity amusing and the use of Functions to be... um, Enlightening