An oft-cited benefit of learning a functional language is that it changes one's approach to solving problems for the better. The functional approach has such a strict emphasis on simplistic and highly composable solutions that an otherwise varied landscape of solution possibilities narrows down to only a few novel options.
This talk introduces functional design and showcases its application to several real-world problems. It will briefly cover denotational semantics and several math-based programming abstractions. Finally, the talk will conclude with a comparison of functional solutions to the results more traditional design methodologies.
No prior knowledge of functional programming or functional programming languages is required for this talk. All the examples make use of the C++ programming language.
4. 4Stellar ScienceDavid Sankel
Quiz
Are the following two C++ programs the same?
#include <iostream>
int main( int argc, char** argv )
{
std::cout << “Hello Worldn”;
}
#include <iostream>
int main( int argc, char** argv )
{
std:: cout << “Hello Worldn”;
}
5. 5Stellar ScienceDavid Sankel
Quiz
Are the following two programs the same?
#include <iostream>
int main( int argc, char** argv )
{
std::cout << “Hello Worldn”;
}
#!/usr/bin/env python
print(“Hello World”)
6. 6Stellar ScienceDavid Sankel
Quiz
Are the following two programs the same?
int f( int c )
{
if( false )
return 45;
else
return c + 5;
}
int f( int c )
{
int j = 5;
j += c;
return j;
}
7. 7Stellar ScienceDavid Sankel
Essence of Programs
Ideally we would like:
• Strong equivalence properties
• Something written down
• A set of rules we can apply to any program
Any ideas of what would be a good essence language?
9. 9Stellar ScienceDavid Sankel
Denotational Semantics
Developed by Dana Scott and Christopher Strachey in late 1960s
Write a mathematical function to convert syntax to meaning (in
math).
μ⟦e₁ + e₂⟧ = μ⟦e₁ ⟧ + μ⟦e₂⟧ where eᵢ is an expression
μ⟦i⟧ = i where i is an integer
11. 11Stellar ScienceDavid Sankel
Function Meaning
We could represent a
function as a set of pairs
As in:
{ …,(-1,4), (0,5), (1,6),…}
Or as a lambda equation: λc. c + 5
Or something else: f(c) = c + 5
int f( int c )
{
if( false )
return 45;
else
return c + 5;
}
13. 13Stellar ScienceDavid Sankel
The Next 700 Programming
Languages
P. J. Landin wrote in 1966 about his programming language
ISWIM (If you See What I Mean)
f(b+2c) + f(2b-c)
where f(x) = x(x+a)
14. 14Stellar ScienceDavid Sankel
Why not drop the C++
nonsense and go Haskell?
Haskell variant with optimizations: 0.41s
C++ variant without optimizations: 0.16s
0
0.2
0.4
0.6
Haskell C++
Quicksort 160,000 ints
16. 16Stellar ScienceDavid Sankel
Semantics Discovery
• Discover the mathematical essence of
a problem and derive an
implementation.
• Conal Elliott, various applications of
semantics discovery throughout career.
• See ‘Denotational design with type
class morphisms’.
17. 17Stellar ScienceDavid Sankel
Ingredients
• Math augmented with some extra constructs can represent
the essence of code.
• Write programs in math.
• C++ straddles more levels of abstraction than any other
language.
• Discover essence of problem using math and derive
implementation.
18. 18Stellar ScienceDavid Sankel
Functional Design
1. Discover the mathematical essence of the problem and write
it out.
2. Derive an efficient implementation in C++ that has the
interface discovered in #1.
22. 22Stellar ScienceDavid Sankel
Product
Given types ‘a’ and ‘b’, the product of ‘a’ and ‘b’ (a ⊗ b) is a type
whose values have an ‘a’ and a ‘b’.
using AAndB = std::pair<A,B>;
using AAndB = std::tuple<A,B>;
struct AAndB {
A a;
B b;
};
24. 24Stellar ScienceDavid Sankel
Sum
A ⊕ B is a type whose values are either a value of type ‘A’ or a value of type
‘B’.
struct AOrB {
bool hasA;
union {
A a;
B b;
} contents; // ‘a’ when ‘hasA==true’
// otherwise ‘b’.
};
using AOrB = boost::variant< A, B >;
using AOrB = std::variant< A, B >; // hopefully
25. 25Stellar ScienceDavid Sankel
Function Type
A → B is a type whose values are pure functions with an input
type A and an return type B.
using FunctionAB = std::function<B (A)>;
26. 26Stellar ScienceDavid Sankel
Meaning
μ⟦ syntax ⟧ = mathExpression
The meaning of “syntax” is the math expression
μ⟦ expression ⟧ : mathExpression
The type of “expression” is the math expression
μ⟦ int ⟧ = ℤ
μ⟦ 3 ⟧ : ℤ
μ⟦ 3 ⟧ = 3
27. 27Stellar ScienceDavid Sankel
Some Examples
μ⟦ boost::optional<e₁> ⟧ = μ⟦ e₁ ⟧ ⊕ 1
μ⟦ std::pair<e₁,e₂> ⟧ = μ⟦ e₁ ⟧ ⊗ μ⟦ e₂ ⟧
μ⟦ double ⟧ = ℝ
or maybe
μ⟦ double ⟧ = ℝ ⊕ 1⊕ 1⊕ 1
where the extra states are -∞, +∞, and NaN
32. 32Stellar ScienceDavid Sankel
What is a stream?
Let ‘Action’ be some side-effecting operation.
μ⟦ sink<e> ⟧ = μ⟦ e ⟧ → Action
μ⟦ source<e> ⟧ = (μ⟦ e ⟧ → Action) → Action
template< typename T >
using sink = std::function<void ( const T & )>;
template< typename T >
using source = std::function<void ( sink<T> ) >;
33. 33Stellar ScienceDavid Sankel
Example source/sink
source<char> consoleInput = []( sink<char> s ) {
int inputChar;
while( (inputChar = std::cin.get()) != EOF ) {
s( static_cast< char >( inputChar ) );
};
sink<char> consoleOutput = []( char c ) {
std::cout.put( c );
};
34. 34Stellar ScienceDavid Sankel
Connecting Sources and Sinks
μ⟦ connect<e> ⟧ : μ⟦ source<e> ⟧ → μ⟦ sink<e> ⟧ → Action
μ⟦ connect<e>( so, si ) ⟧ = μ⟦ so ⟧( μ⟦ si ⟧ )
template< typename t >
void connect( source<t> so, sink<t> si ) {
so( si );
}
int main( int argc, char** argv ) {
connect( consoleInput, consoleOutput );
}
35. 35Stellar ScienceDavid Sankel
Transforming Streams
μ⟦ sink<e> ⟧ = μ⟦ e ⟧ → Action
μ⟦ transform<a,b> ⟧ = μ⟦ Sink<b> ⟧ → μ⟦ Sink<a> ⟧
= μ⟦ Sink<b> ⟧ → (μ⟦ a ⟧ → Action)
= μ⟦ Sink<b> ⟧ → μ⟦ a ⟧ → Action
template< typename a, typename b >
using transform = std::function<void ( sink<b>, a ) >;
36. 36Stellar ScienceDavid Sankel
Application of Transforms
μ⟦ transform<a,b> ⟧ = μ⟦ sink<b> ⟧ → μ⟦ a ⟧ → Action
μ⟦ applyToSink<a,b> ⟧
: μ⟦ transform<a,b> ⟧ → μ⟦ sink<b> ⟧ → μ⟦ sink<a> ⟧
μ⟦ applyToSource<a,b> ⟧
: μ⟦ transform<a,b> ⟧ → μ⟦ source<a> ⟧ → μ⟦ source<b> ⟧
μ⟦ so >> t ⟧ = μ⟦ applyToSource<a,b> ⟧( t, so );
μ⟦ t >> si ⟧ = μ⟦ applyToSink<a,b> ⟧( t, si );
μ⟦ so >> si ⟧ = μ⟦ connect<t> ⟧( so, si );
The designs that produced with the methodology we will develop, will not only be simple and concise, but they’ll approach a purity where you start to wonder if you’re getting close to an optimal design.
Here although the individual files are different, the syntax tree is the same. We have syntactic equivalence.
The one on the right is Python. Note that python outputs a newline with every print statement. These are not syntactically equivalent. We can say they are the same because ‘diff’ says the output of these two programs is the same.
Clearly not syntactically equivalent. Neither of these programs have output we can compare using diff. Here we make a mental representation of both of these programs. That mental representation has the strong equivalence property. We mentally remove the extraneous noise. We check that and give an answer. That won’t help convince someone else of the equivalence though. It would be really nice if we could write down this intermediate format.
Hint: you learned this language in grade school.
All of these have the strong equality guarantee provided by math.
We can come up with math that defines how bottom interacts with everything else. A bottom statement followed by another statement has a meaning of bottom, etc.
We see that there is an art here to extending the math used in the “semantic domain” to capture
The idea here was to make a programming language based on mathematical notation. Note how Haskell is the modern offspring of this idea that all the kids are using these days. Comment about how ‘monads’ ‘monoids’ ‘categories’, etc. are essential to Haskell’s roots of an attempt to be a mathematics computer language. Also interesting how Landin called his language “see what I mean” before Scott Strachey’s rigorous definition of meaning.
I just asked google for an implementation of each and ran them on 160,000 ints.
Can the Haskell version be optimized to go just as fast as the C++ one? Probably, but then it will loose what makes Haskell the math language.
With Haskell we get all the expressivity, but we pay the price of not being able to understand or easily tweak the runtime behavior.
Just as a hint, Agda is even worse!
The low-level represents ability to speak to the machine in the language most natural to it. High-level indicates ability to speak to the programmer succinctly and abstractly (math). In other words, the higher level languages relate more closely to the essence of the problem.
We don’t want to use Haskell basically because we don’t want to be limited by its syntax.
Discuss getopt and boost’s program options libraries. What if I want to use single rather than double dash? What if I want positional arguments to effect what comes after? What is it that I really want? If wanting these options requires yet another feature and complexity be added to the library, that is a good indicator that the semantic domain wasn’t nailed in the first place. Time to start over.
Discuss getopt and boost’s program options libraries. What if I want to use single rather than double dash? What if I want positional arguments to effect what comes after? What is it that I really want? If wanting these options requires yet another feature and complexity be added to the library, that is a good indicator that the semantic domain wasn’t nailed in the first place. Time to start over.
Flexible, composable, safe.
Denotational semantics, but reversed of how they intended it. We go from the high-level math to the low-level implementation.