Orthogonal Functional
Architecture
Lambda Squared - Knoxville, TN
John A. De Goes — @jdegoes
http://degoes.net
Introduction
The Dysfunctional Nightmare abandon all
hope ye who
enter
Procedural Code
Ad hoc solutions constructed from large number of non-composable effects that
are reasoned about non-locally.
The Functional Dream bliss awaits all
ye who enter
Functional Code
Principled solutions constructed from a small number of composable building
blocks that are reasoned about locally.
Two Keys to Bliss
Orthogonality + Composability
Two Keys to Bliss
Composability makes functional
code powerful, and
orthogonality makes it
beautiful*
*i.e. modular and uncluttered by irrelevant details.
Two Keys to Bliss
Composable, orthogonal bases
tend to be powerful, but small
and simple to reason about,
permitting flexible, modular
solutions to many problems.
Composability
Composability
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException;
}
Is this Java Future composable?
Composability
Composability measures the
extent to which values can be
combined with other values to
produce like values
Composability
1 + 1 = 2
Two integers combine to yield another integer.
1 – 1 = 0
Integers are composable with respect to addition/subtraction.
Composability
Non-Composable Composable
Composability
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException;
}
Is this Java Future composable?
Orthogonality
Orthogonality
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException;
}
Is this Java Future orthogonal?
Orthogonality
Orthogonality measures the
extent to which primitive
operations on values have
single, unique concerns
Orthogonality
Addition moves right
Addition/subtraction are orthogonal
Subtraction moves left
Orthogonality
A
A + B B
A
Non-Orthogonal Orthogonal
B
Orthogonality
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException;
}
Is this orthogonal?
Orthogonality
public interface Future<V> {
boolean cancel(
boolean mayInterruptIfRunning);
boolean isDone();
V get() throws
InterruptedException,
ExecutionException;
V get(long timeout, TimeUnit unit)
throws
InterruptedException,
ExecutionException,
TimeoutException;
}
Is this orthogonal?
Timeout
Get
Timeout + Get
Orthogonality
The cardinal sin of
non-orthogonality is the
tangling of separate concerns,
which infects the code base to
destroy modularity.
Orthogonality
Process
Steps Toward Orthogonality
1. Make the system composable.
Steps Toward Orthogonality
2. Identify the primitive
operations.
Steps Toward Orthogonality
3. Identify the unique concerns.
Steps Toward Orthogonality
4. Refactor the primitive
operations until each has a
unique concern.
Worked Examples
Worked Examples
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException,ExecutionException,
TimeoutException;
}
Is this orthogonal?
Worked Examples
The curse of non-orthogonality!
// Would like to write:
<A> Future<A> retryUntilSuccess(Future<A> future, Interval spacing) {
// ???
}
// Forced to write:
A retryUntilSuccess(Future<A> future, Interval spacing,
long timeout, TimeUnit unit) {
// ...
}
Worked Examples
public interface Future<V> {
Future<Tuple2<Future<V>, Function<Boolean, Future<Unit>>>> fork();
V unsafePerformGet() throws InterruptedException, ExecutionException;
Future<V> timeout(long timeout, TimeUnit unit);
}
Future 2.0: Detangle ‘get’ and ‘timeout’
Worked Examples
Is this orthogonal?
static int compare(
String str1,
String str2,
boolean nullIsLess)
Worked Examples
Is this orthogonal?
NullIsLess +
Comparison
NullIsLess
Comparison
static int compare(
String str1,
String str2,
boolean nullIsLess)
Worked Examples
The curse of non-orthogonality!
List<String> myAlgorithm(List<String> list, boolean nullIsFirst) {
// ...
int c = compare(first, second, nullIsFirst);
// ...
}
enum Ordering { LT, EQ, GT }
interface Ord<A> {
Ordering compare(A l, A r);
}
Worked Examples
1. Define a unit of composition
class StringOrd extends Ord<String> {
public Ordering compare(String l, String r) {
// ...
}
}
Worked Examples
2. Define one dimension (string comparison)
class NullIsLess<A> {
private final Ord<A> ord;
public NullIsLess(Ord<A> ord) {
this.ord = ord;
}
public Ordering compare(A l, A r) {
if (l == null) { if (r == null) return EQ; else return LT; }
else if (r == null) return GT;
else return ord.compare(l, r);
}
}
Worked Examples
3. Define another dimension (null is first)
List<String> myAlgorithm(List<String> list, Ord<String> ord) {
// ...
Ordering c = ord.compare(first, second); // <- Beautiful!!!
// ...
}
Ord<String> ord = new NullIsLess<String>(new StringOrd());
List<String> list2 = myAlgorithm(list, ord);
Worked Examples
4. Compose orthogonal components
Worked Examples
Is this orthogonal?*
data MVar a
putMVar :: MVar a -> a -> IO ()
takeMVar :: MVar a -> IO a
*Thanks to Fabio the fabulous for this example.
Worked Examples
Is this orthogonal?
data MVar a
putMVar :: MVar a -> a -> IO ()
takeMVar :: MVar a -> IO a
Synchronization +
Concurrency
Synchronization
Concurrency
Worked Examples
data IORef a
newtype Expect a = Expect a
modify ::
IORef a -> (a -> a) -> IO Bool
data Promise a
newPromise :: IO (Promise a, a -> IO ())
awaitPromise :: Promise a -> IO a
Synchronization
(IORef)
Concurrency
(Promise)
Worked Examples
Is this orthogonal?*
*A tiny part of Apache String Utils.
Worked Examples
Is this orthogonal?
?
?
Worked Examples
Is this orthogonal?
data Parser a = Parser (String -> Either String (String, a))
char :: Parser Char
fail :: String -> Parser a
alt :: Parser a -> Parser a -> Parser a
seq :: Parser a -> (a -> Parser b) -> Parser b
pure :: a -> Parser a
map :: (a -> b) -> Parser a -> Parser b
Worked Examples
Is this orthogonal?*
data Parser a = Parser (String -> Either String (String, a))
char :: Parser Char
fail :: String -> Parser a
alt :: Parser a -> Parser a -> Parser a
seq :: Parser a -> (a -> Parser b) -> Parser b
pure :: a -> Parser a
map :: (a -> b) -> Parser a -> Parser b
*Trick question — or is it?
Worked Examples
Is this orthogonal?
data Parser a = Parser (String -> ...
char :: Parser Char
fail :: String -> Parser a
alt :: Parser a -> Parser a -> Parser a
seq :: Parser a -> (a -> Parser b) -> Parser b
pure :: a -> Parser a
map :: (a -> b) -> Parser a -> Parser b
SequencingMapping
Flattening
Worked Examples
Is this orthogonal?
data Parser a = Parser (String -> ...
char :: Parser Char
fail :: String -> Parser a
alt :: Parser a -> Parser a -> Parser a
join :: Parser (Parser a) -> Parser a
pure :: a -> Parser a
map :: (a -> b) -> Parser a -> Parser b
Mapping
Flattening
Wrap
Summary
1. Functional code is composable and orthogonal, allowing a small set of
principled building blocks to snap together to solve complex problems in
a predictable, reasonable way.
2. Composability measures the combinability of values.
3. Orthogonality measures the singular focus of primitive operations.
4. Refactor to orthogonality to obtain modular clode, uncluttered by
irrelevant details.
Thank You!
Special thanks to Reid, Cameron, Emily, & the wonderful Knoxville FP
community, and the generous sponsor ResultStack!
John A. De Goes — @jdegoes
http://degoes.net

Orthogonal Functional Architecture

  • 1.
    Orthogonal Functional Architecture Lambda Squared- Knoxville, TN John A. De Goes — @jdegoes http://degoes.net
  • 2.
  • 3.
    The Dysfunctional Nightmareabandon all hope ye who enter
  • 4.
    Procedural Code Ad hocsolutions constructed from large number of non-composable effects that are reasoned about non-locally.
  • 5.
    The Functional Dreambliss awaits all ye who enter
  • 6.
    Functional Code Principled solutionsconstructed from a small number of composable building blocks that are reasoned about locally.
  • 7.
    Two Keys toBliss Orthogonality + Composability
  • 8.
    Two Keys toBliss Composability makes functional code powerful, and orthogonality makes it beautiful* *i.e. modular and uncluttered by irrelevant details.
  • 9.
    Two Keys toBliss Composable, orthogonal bases tend to be powerful, but small and simple to reason about, permitting flexible, modular solutions to many problems.
  • 10.
  • 11.
    Composability public interface Future<V>{ boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } Is this Java Future composable?
  • 12.
    Composability Composability measures the extentto which values can be combined with other values to produce like values
  • 13.
    Composability 1 + 1= 2 Two integers combine to yield another integer. 1 – 1 = 0 Integers are composable with respect to addition/subtraction.
  • 14.
  • 15.
    Composability public interface Future<V>{ boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } Is this Java Future composable?
  • 16.
  • 17.
    Orthogonality public interface Future<V>{ boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } Is this Java Future orthogonal?
  • 18.
    Orthogonality Orthogonality measures the extentto which primitive operations on values have single, unique concerns
  • 19.
    Orthogonality Addition moves right Addition/subtractionare orthogonal Subtraction moves left
  • 20.
    Orthogonality A A + BB A Non-Orthogonal Orthogonal B
  • 21.
    Orthogonality public interface Future<V>{ boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } Is this orthogonal?
  • 22.
    Orthogonality public interface Future<V>{ boolean cancel( boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } Is this orthogonal? Timeout Get Timeout + Get
  • 23.
    Orthogonality The cardinal sinof non-orthogonality is the tangling of separate concerns, which infects the code base to destroy modularity.
  • 24.
  • 25.
  • 26.
    Steps Toward Orthogonality 1.Make the system composable.
  • 27.
    Steps Toward Orthogonality 2.Identify the primitive operations.
  • 28.
    Steps Toward Orthogonality 3.Identify the unique concerns.
  • 29.
    Steps Toward Orthogonality 4.Refactor the primitive operations until each has a unique concern.
  • 30.
  • 31.
    Worked Examples public interfaceFuture<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException,ExecutionException, TimeoutException; } Is this orthogonal?
  • 32.
    Worked Examples The curseof non-orthogonality! // Would like to write: <A> Future<A> retryUntilSuccess(Future<A> future, Interval spacing) { // ??? } // Forced to write: A retryUntilSuccess(Future<A> future, Interval spacing, long timeout, TimeUnit unit) { // ... }
  • 33.
    Worked Examples public interfaceFuture<V> { Future<Tuple2<Future<V>, Function<Boolean, Future<Unit>>>> fork(); V unsafePerformGet() throws InterruptedException, ExecutionException; Future<V> timeout(long timeout, TimeUnit unit); } Future 2.0: Detangle ‘get’ and ‘timeout’
  • 34.
    Worked Examples Is thisorthogonal? static int compare( String str1, String str2, boolean nullIsLess)
  • 35.
    Worked Examples Is thisorthogonal? NullIsLess + Comparison NullIsLess Comparison static int compare( String str1, String str2, boolean nullIsLess)
  • 36.
    Worked Examples The curseof non-orthogonality! List<String> myAlgorithm(List<String> list, boolean nullIsFirst) { // ... int c = compare(first, second, nullIsFirst); // ... }
  • 37.
    enum Ordering {LT, EQ, GT } interface Ord<A> { Ordering compare(A l, A r); } Worked Examples 1. Define a unit of composition
  • 38.
    class StringOrd extendsOrd<String> { public Ordering compare(String l, String r) { // ... } } Worked Examples 2. Define one dimension (string comparison)
  • 39.
    class NullIsLess<A> { privatefinal Ord<A> ord; public NullIsLess(Ord<A> ord) { this.ord = ord; } public Ordering compare(A l, A r) { if (l == null) { if (r == null) return EQ; else return LT; } else if (r == null) return GT; else return ord.compare(l, r); } } Worked Examples 3. Define another dimension (null is first)
  • 40.
    List<String> myAlgorithm(List<String> list,Ord<String> ord) { // ... Ordering c = ord.compare(first, second); // <- Beautiful!!! // ... } Ord<String> ord = new NullIsLess<String>(new StringOrd()); List<String> list2 = myAlgorithm(list, ord); Worked Examples 4. Compose orthogonal components
  • 41.
    Worked Examples Is thisorthogonal?* data MVar a putMVar :: MVar a -> a -> IO () takeMVar :: MVar a -> IO a *Thanks to Fabio the fabulous for this example.
  • 42.
    Worked Examples Is thisorthogonal? data MVar a putMVar :: MVar a -> a -> IO () takeMVar :: MVar a -> IO a Synchronization + Concurrency Synchronization Concurrency
  • 43.
    Worked Examples data IORefa newtype Expect a = Expect a modify :: IORef a -> (a -> a) -> IO Bool data Promise a newPromise :: IO (Promise a, a -> IO ()) awaitPromise :: Promise a -> IO a Synchronization (IORef) Concurrency (Promise)
  • 44.
    Worked Examples Is thisorthogonal?* *A tiny part of Apache String Utils.
  • 45.
    Worked Examples Is thisorthogonal? ? ?
  • 46.
    Worked Examples Is thisorthogonal? data Parser a = Parser (String -> Either String (String, a)) char :: Parser Char fail :: String -> Parser a alt :: Parser a -> Parser a -> Parser a seq :: Parser a -> (a -> Parser b) -> Parser b pure :: a -> Parser a map :: (a -> b) -> Parser a -> Parser b
  • 47.
    Worked Examples Is thisorthogonal?* data Parser a = Parser (String -> Either String (String, a)) char :: Parser Char fail :: String -> Parser a alt :: Parser a -> Parser a -> Parser a seq :: Parser a -> (a -> Parser b) -> Parser b pure :: a -> Parser a map :: (a -> b) -> Parser a -> Parser b *Trick question — or is it?
  • 48.
    Worked Examples Is thisorthogonal? data Parser a = Parser (String -> ... char :: Parser Char fail :: String -> Parser a alt :: Parser a -> Parser a -> Parser a seq :: Parser a -> (a -> Parser b) -> Parser b pure :: a -> Parser a map :: (a -> b) -> Parser a -> Parser b SequencingMapping Flattening
  • 49.
    Worked Examples Is thisorthogonal? data Parser a = Parser (String -> ... char :: Parser Char fail :: String -> Parser a alt :: Parser a -> Parser a -> Parser a join :: Parser (Parser a) -> Parser a pure :: a -> Parser a map :: (a -> b) -> Parser a -> Parser b Mapping Flattening
  • 50.
  • 51.
    Summary 1. Functional codeis composable and orthogonal, allowing a small set of principled building blocks to snap together to solve complex problems in a predictable, reasonable way. 2. Composability measures the combinability of values. 3. Orthogonality measures the singular focus of primitive operations. 4. Refactor to orthogonality to obtain modular clode, uncluttered by irrelevant details.
  • 52.
    Thank You! Special thanksto Reid, Cameron, Emily, & the wonderful Knoxville FP community, and the generous sponsor ResultStack! John A. De Goes — @jdegoes http://degoes.net