Functional Thread 4

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
6,282
Reaction score
405
Location
Review of previous functional threads
To start I'll provide for reference purposes a set of link to the other functionally oriented threads I have posted (as requested):

Ok, .... so now for the scope of this thread:
We'll review the following topics:
  • Parametricity: e.g. parametric polymorphic functions -- Every function of the same type satisfies the same theorem, incl. Liskov's substitution principle.
  • Variance: Covariance, Contravariance
  • Maps, maps, and more maps... (map, flatmap, mapreduce, contramap, bimap, ...)
This is intended to be a beginner's friendly thread.


As for a challenge:
Please submit your code implementation and / or usage examples of the various types of map functions that we encounter in every day code and / or those that are easy to implement by yourself. Plus points for describing why this improves the code we write.
 
Last edited:
Mathematical Variance
When we encounter the use of the contravariance and covariance in mathematics it might not be immediately obvious that the difference is related to changes in:
  • outputs: covariant
  • inputs: contravariant
... but if we explore this, we'll find that this is exactly what it is.


Changing the Output
The image reflects what happens when we apply changes to the output of a function:
  • in the blue line; when we add 1 the sine wave moves up the y axis by 1.
  • similarly with the red line; when we subtract 1 the sine wave moves down the y axis by 1.
Basically the effect is mutually proportional to the change we apply to our function's output.

Covariance.png


Changing the Input
The image below reflects what happens when we apply changes to the input of a function:
  • in the blue line; when we add 1 the sine wave moves backwards on the x axis.
  • similarly with the red line; when we subtract 1 the sine wave moves forward on the x axis.
Basically the effect is disproportionate (or opposing) to the change we make to our function's input.

Contravariance.png
 
Last edited:
Variance with Objects
The common use of the terms contravariance and covariance in programming has come to mean something a little more specific than mathematics but is still often related to the inputs and outputs of functions.

Inheritance.png

The whole idea relates to the hierarchy of types. If type B is derived from type A that is it "inherits" from A or, put another way, A is the base class for B - then it contains every method and property that type A does and more.

Essentially B is "bigger" than type A, and this plays out in the ordering of the classes.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]The notion that B is "bigger" than A is important, however how we express this in type theory can be initiallu confusing; in that we use the less than sign > to show that a type is derived from another. for example:

A > B[/td]
[/tr]
[/table]

We can remove some of the confusion if we use the less than sign to as an indicator of class hierarchy; i.e. A has a "higher" or "greater" position in the type hierarchy than B, or alternatively visualise the less than sign as an arrow that implies that everything that A has is inherited by B.


Substitution Principle

If B is derived from A, then A > B; basically B inherits or contains everything that A has.

So if B has everything that A has, we could use B as a substitute for any computation requiring an A; however the reverse isn't always true because B may have additional properties and methods

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]This relationship is asymmetrical, and is formally known as the Liskov Substitution Principle.

More so this style of variance with objects is referred to as subtype polymorphism.[/td]
[/tr]
[/table]


Inferred Types
We can use hierarchical relationships to state that if B can be used anywhere a type A is required; then A can be seen as a base type for B, or B < A; or B is below A in the type hierarchy.


Methods

Now let's examine what happens when we apply this subtype polymorphism to methods of an object.

PHP:
class xyz {
  static B method1(A a) {
    return new(B)
  }
}

We could use this as follows:

PHP:
  A myA = xyz.method1(new(B))

i.e. if A > B, then the Input parameter A can be B because B has inherited everything that A has; but by the usual rules the result type of this method B can inversely be treated as an A.


Derivation using input parameters:

PHP:
class xyz { 
  void method1(A a) { 
    ...
  } 

  void method2(B b) { 
    ...
  } 
}

So by the substitution principle method1 can accept B as its input parameter; and by reverse substitution principle we can envisage method1 as being derived from method2;

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]
i.e. by changing the input parameter A to a B where A > B would result in a method hierarchy of:

method1 < method2
[/td]
[/tr]
[/table]


Derivation using output parameters:

PHP:
class xyz { 
  A method1() { 
    return new(A)
  } 
      
  B method2() { 
    return new(B)
  } 
}

In this example method2 can be substituted in any place where method1 is required, because it returns a B which inherits from A, and hence method2 can be considered as being derived from method1

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]
i.e. by substituting a B output in place of A would result in a method hierarchy of:

method1 > method2
[/td]
[/tr]
[/table]

Conclusion
By changing the output type from A to B increases the derivation and hence the result is covariant; whereas changing the input parameter to be more derived makes the result less derived i.e. contravariant.

Essentially this means that an input of A can be replaced by any of its subclasses and the output of B can be replaced by any of its superclasses.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]
A very typical use of variance / substitution principle is with delegation; i.e. the evaluating of a property or method of one object in the context of another.
[/td]
[/tr]
[/table]

... the next post will examine variance as it relates to parametric polymorphism, otherwise known as generics. As part of that post we will also explore a typical use case for contravariance, including a map that you probably would not have encountered i.e. contramap.
 
Last edited:
Ok so now that we covered for most parts the base theory of variance; let's look at a practical code example of covariance vs. contravariance.

Covariance is easy
Covariance is both easy to understand and easy to employ in your code; because it's the "normal" for function application, for example:

PHP:
func increment(_ x: Int) -> Int {
  return x + 1
}

func square(_ x: Int) -> Int {
  return  x * x
}

// usage
let input = 1
let result = square(increment(input))
print(result) // 4

In the above code we have 2 functions:
  • increment: adds 1 to its input parameter value
  • square: squares its input parameter value
In the usage example, we start with a variable input with a value of 1; and with result we have pass the input to increment (standard function application), and then the output of that is finally applied to the square function.

This is an example of a covariance, because we are working with outputs i.e. the output of our variable is fed into the increment function, and the output of that is fed into our square function. This mapping over outputs is covariance, and as we saw in the sine graph, the result is mutually proportional to the change we effect to the outputs.

Let's look at another example using an array of integers:
PHP:
let input = [0, 1, 2, 3, 4, 5, 6]
let result = input.map { pow(2, $0) }  // $0 is syntactic sugar for the first element value being iterated in. 
print(result) // [1, 2, 4, 8, 16, 32, 64]
Our map method is what we refer to as a covariant functor; i.e. it's a higher order function that takes as function / computation as its input and then iterates over the elements, applying the computation to each element within that container type: the Array.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]In a later post we'll look in detail at the internal workings of map, in comparison with a standard for-loop.[/td]
[/tr]
[/table]

Contravariance can be a bit confusing
Contravariance can be a bit confusing because it's not the "natural" way functional application works. As we saw in the sine graphs it's about affecting changes to the inputs of a computation, and is additionally confusing because it's not proportional or mutual to the change we make. In the sine graph we saw that affecting changes to the input had an opposing effect that was also not proportional in value to the change we applied.

Now if that wasn't confusing enough, we also need to appreciate that because contravariance only works with inputs; we won't be able use it in a majority of situations where covariance is natural to use.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Contravariance essentially only makes sense for data types that encapsulate computations; because we are effecting changes to a computation before it's applied to an input value.[/td]
[/tr]
[/table]

Let's define a type that encapsulates a computation
A good example of a this is a Predicate; i.e. a Boolean computation that evaluates to either True to False
PHP:
struct Predicate<A> {
  let contains: (A) -> Bool
}
In Swift a struct is like class except that it employs value semantics over reference semantics; you can however implement this as a class and it will produce the same result. So basically we've created a type called Predicate that is generic over a single type; our placeholder variable being A can represent any type (including those we define) e.g. Int, String, Double, User, Employee, etc.

Internally we have a single property called contains that is a variable that holds onto a boolean computation. Note: In Swift the initialisers / constructors for structs are automatically generated by the compiler. i.e. this struct has a init method that we can use setup a Predicate value, for example:

PHP:
let lessThan10 = Predicate<Int>(contains: { x in x < 10 })

// Usage examples
print(lessThan10.contains(23)) // false
print(lessThan10.contains(8)) // true
Using this new predicate variable; works just like accessing a getter on our property called contains, except that we have to pass in the expected value for the computation that is held by our Predicate variable i.e. we need to pass in an Integer value.

This works similar to logic conditions that we typically evaluate in a if...else type clause, and similarly we can modify our predicate type to allow conjunctions of more than 1 Predicate.

We'll implement this by making a change to our predicate type to essentially allow concatenation of Predicate types, for example:

PHP:
struct Predicate<A> {
  let contains: (A) -> Bool

  func concatenate(_ with: Predicate<A>) -> Predicate<A> {
    return Predicate<A>(contains: {x in self.contains(x) && with.contains(x) })
  }
}
In the above new version of Predicate we have added a method called concatenate that has a single argument; a second Predicate which we will logically concatenate with our existing Predicate. In the internals we basically created a new Predicate that is the logical (and) combined result of both Predicates by applying the same input value to both Predicates.

Here's an example of how we use this.
PHP:
let lessThan10 = Predicate<Int>(contains: { x in x < 10 })
let greaterThan5 = Predicate<Int>(contains: { x in x > 5 })

// let's combine these two Predicates
let combined = lessThan10.concatenate(greaterThan5)

// and finally let's test this with a few integer values
print(combined.contains(5)) // false
print(combined.contains(6)) // true
print(combined.contains(10))  // false
print(combined.contains(9)) // true

We can also define an operator for this; i.e. to simplify both the concatenation of predicates and composition, basically by not having to wrap everything in brackets. Let's add concatenation operator to our Predicate type.

PHP:
struct Predicate<A> {
  let contains: (A) -> Bool
  
  func concatenate(_ with: Predicate<A>) -> Predicate<A> {
    return Predicate<A>(contains: {x in self.contains(x) && with.contains(x) })
  }
  
  static func <>(lhs: Predicate<A>, rhs: Predicate<A>) -> Predicate<A> {
    return lhs.concatenate(rhs)
  }
}

...and let's see how that changes our usage:
PHP:
// instead of .dot method chaining
let combined = lessThan10.concatenate(greaterThan5)

// we can use our new <> concatenation operator
let combined = lessThan10 <> greaterThan5

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Note: The <> operator is derived from the algebraic term Semigroup; which is an algebraic structure with an associative binary operation e.g. addition, multiplication, logical conjunction, etc.
[/td]
[/tr]
[/table]

Let's add Contravariance
Ok without getting too complicated, let's using a very simple example, examine how we can apply an effect to our Predicate's input; in much the same way that an effect was applied to the 2nd sine graph.

First off let's define a predicate for all the odd Integers
PHP:
let isOdd = Predicate<Int>(contains: { $0 % 2 == 1 })

Now to create a Predicate for all odd Integers we can do either of the following:

Option 1:
We create a new predicate where the closure evaluates odd numbers using modulus of 2
PHP:
let isOdd = Predicate<Int>(contains: { $0 % 2 == 0 })

Option 2:
We create a new predicate by adjusting the input of the isEven predicate; basically we add 1 to its input to derive an isOdd
PHP:
let isOdd = Predicate<Int>(contains: { x in isEven.contains(x + 1) })
This 2nd option is an example of contravariance; because we are adjusting the input before it enters the isEven.contain computation.

There is however a more elegant way to achieve this -- we can create a higher order method like map to contravariantly adjust the input to a computation; aptly the most appropriate name for this method is contramap or alternatively comap.

Let's modify our Predicate to include a contramap method:
PHP:
struct Predicate<A> {
  let contains: (A) -> Bool
  func contramap<B>(_ f: escaping (B) -> A) -> Predicate<B> {
    return Predicate<B>(contains: f >>> self.contains)
  }
  
  func concatenate(_ with: Predicate<A>) -> Predicate<A> {
    return Predicate<A>(contains: {x in self.contains(x) && with.contains(x) })
  }
  
  static func <>(lhs: Predicate<A>, rhs: Predicate<A>) -> Predicate<A> {
    return lhs.concatenate(rhs)
  }
}
Ok we've added a new method called contramap, that essentially feeds (or injects) a modification into our Predicate's type (A) ahead of its own computation; we achieve this by forward composing our new function from (B) to a new A with the computation that our Predicate is currently holding onto in its contains method. Essentially this allows us to adjust the input before the computation.

Let's finally for this post examine how we would create an isOdd predicate by modifying the input of isEven using our new contramap method.
PHP:
let isOdd = isEven.contramap { $0 + 1 }
...and there we have it a higher order function that allows us to easily contravariantly inject changes into the input of a computation i.e. the one we're holding onto in our Predicate type.

In the next post we'll examine a more elaborate usage case for our new contramap method.
 
Last edited:
Thanks for this, busy catching up with Thread 3 first though. :)
Always a pleasure.
Hope you're at least making some sense from my ramblings; these FP bits are a lot like OOP starting out; very weird and a quite a lot of terminology / acronym overload.

As for this thread:
There'll be a few more posts in this thread around the different types of maps; still a bit of personal debate about how far I'll extend this; certainly won't cover everything as that'll no longer make this a "beginner" friendlier thread, but I should at least finish off:
  • covariant functors -- the default map
  • contravariant functors - contramap
  • monad - flatMap (the simple version)


As for future threads:
Map areas like: bimap, dimap, imap, profunctors, ... are all topics for a later date.

Except I'll probably skirt around the concept of Lenses (profunctors) with my next contramap post re its kind of difficult to make most FP concepts interesting when on their own they're rather simplistic notions; and their underlying power is quite often overlooked without a more comprehensive example - catch 22 -- because too much complexity too soon is its own worst enemy..
 
Last edited:
Usage example for contramap
Scenario: Employee web service feeds into a UI with custom filtering:

Data struct + decoding json:
PHP:
// JSON data example.
let json = """
  [
    { "id": 123, "name": "Zanetello", "salary": 9500 },
    { "id": 17, "name": "Becker", "salary": 1000 },
    { "id": 21, "name": "Jameson", "salary": 6000 },
    { "id": 36, "name": "Edwards", "salary": 3000 },
    { "id": 40, "name": "Spencer", "salary": 4000 },
    { "id": 54, "name": "Timol", "salary": 1500 },
    { "id": 61, "name": "Norman", "salary": 2300 },
    { "id": 13, "name": "Abrahams", "salary": 4100 },
    { "id": 102, "name": "Victor", "salary": 5140 }
  ]
""".data(using: .utf8)

struct Employee: Decodable {
  let id: Int
  let name: String
  let salary: Int
  
  static func decode(_ json: Data? ) throws -> [Employee] {
    return try JSONDecoder().decode([Employee].self, from: json!)
  }
}

let employees = try? Employee.decode(json)

Filter logic helper function examples:
PHP:
let lessEqual = { (x: Int) in Predicate { $0 <= x } }
let less = { (x: Int) in Predicate { $0 < x } }
let greaterEqual = { (x: Int) in Predicate { $0 >= x } }
let greater = { (x: Int) in Predicate { $0 > x } }
let prefixRange = { (x: ClosedRange<String>) in Predicate<String> { x.contains(String($0.first!)) }}

Swift Keypath (Lens) helpers; converts a keypath into an Employee getter function:
PHP:
public func getter<Root, Value>(_ path: KeyPath<Root, Value>) -> (Root) -> Value {
  return { $0[keyPath: path]  }
}

extension KeyPath {
  public static prefix func ^ (rhs: KeyPath) -> (Root) -> Value {
    return getter(rhs)
  }
}

Finally, we translate the custom filter rules (from the UI) into an array of Predicates; the eval function uses contramap to inject the Employee key path getters ahead of the helper logic predicates:
PHP:
func eval<A, B>(_ getter: escaping (B) -> A, _ predicate: Predicate<A>) -> Predicate<B> {
  return predicate.contramap(getter)
}

// Array built from UI selections
let predicates = [eval(^\Employee.name, prefixRange("K"..."Z")),
                  eval(^\Employee.salary, lessEqual(6000)),
                  eval(^\Employee.id, greater(30))]

// reduce over the predicates, monoidally concatenating them to produce a single combination selection predicate.
let selection = predicates.reduce(Predicate<Employee> { _ in true }, <>).contains

print(employees?.filter(selection) ?? "")

The output from this is:
Code:
[Employee(id: 40, name: "Spencer", salary: 4000), Employee(id: 54, name: "Timol", salary: 1500), Employee(id: 61, name: "Norman", salary: 2300), Employee(id: 102, name: "Victor", salary: 5140)]

Some extras
The eval function really only serves two purposes here:
  • Switches the order of the arguments to help with readability
  • Lightens the load on the Swift compiler's type checker
We could just as easily have written our code as follows, and avoid the need for eval altogether.
PHP:
let employees = try? Employee.decode(json)
let predicates = [prefixRange("K"..."Z").contramap(^\Employee.name),
                  lessEqual(6000).contramap(^\Employee.salary),
                  greater(30).contramap(^\Employee.id)]
let selection = predicates.reduce(Predicate<Employee> { _ in true }, <>).contains

print(employees?.filter(selection) ?? "")

[table="width: 90%, class: outer_border, align: center"]
[tr]
[td]Note: Although Swift has first class language support for Lenses (composable getters & setters); almost every other popular language has a Lens framework available for use. Here for example is a link to an article on using Lenses in Javascript: https://medium.com/@gcanti/introduction-to-optics-lenses-and-prisms-3230e73bfcfe

Lenses (a short(er) summary)
...are essentially the Functional equivalent of the getters and setters you find with reference type like class; however they're actually much more than that as they allow us to reach deeply into a data structure to not only get values, but also to change them; all of which is not so simple to achieve even with reference semantics.

Areas where Lenses and Prisms excel (over reference types) is:
  • Composability: lenses and prisms compose together just like functions.
  • They allow us to reach into a nested data structure to (get or set), and even provide easy solutions to map over container elements that are embedded in these data structures. e.g. Container types like arrays, optionals, etc.
  • Immutability provides not only consistency and ease of testing; it also makes it a lot of easier for concurrency. i.e. something that's a lot harder to achieve with reference semantics

How to interpret Swift KeyPaths in the contramap code:
In the contramap code examples; you would have see the following syntax ^\Employee.name, this is a combination of a prefix operator ^ which is used to convert a Lens keypath into function getter; whereas the \Employee.name is the keypath to the property that we want to access. These keypaths are very similar to navigating through reference semantics, e.g. \Person.hobbies[0].name, would provide a keypath to the 1st array element of the hobbies property in Person, and finally to its name property.

If we use the following code in Swift, we can evaluate what types these keypaths are returning, for example:
PHP:
let name = \Employee.name
print(type(of: name)) \\ WritableKeyPath<Employee, String>
Basically it determined that the name property can be accessed with a keypath from the types Employee to String; re the name property is a String type in the Employee data structure.

We can however go a step further by using the ^ caret helper operator (see previous definition) to convert this keypath to a proper getter function as follows:
PHP:
let name = ^\Employee.name
print(type(of: name)) \\ (Employee) -> String
As you can see in the above code, all we've done is add the ^ operator in front of our keypath, and now we get a proper function that takes an Employee parameter and returns a String i.e. a getter function to access the name property in Employee
[/td]
[/tr]
[/table]
 
Last edited:
Here's Optionally a little bit extra information on operators, including defining some extra ones for contramap.

Operators for contramap
Taking prior art direction from Haskell, we find that Haskell has defined the following operator for contramap:
PHP:
(>$) :: b -> f b -> f a infixl 4

Which makes sense if we consider two things:
  • 1st, the $ operator in Haskell is normal function application i.e. functions wrap around their input in a right to left reading order, for example: square(increment(2))
  • 2nd, the >, less than symbol as you seen from earlier in this post is the symbol we associate with variance, i.e. derivation from another type. Again we can also choose to read it as: "we are injecting some functionality ahead of normal function application"

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Side Note: in F#, Scala, OCaml, and others... and you would also have seen in my post on functions, we tend to use the <|; pipe backward operator to refer to normal function application i.e. our equivalent of Haskell's $, and we use |> for as pipe forward operator to reverse direction to make things a bit more readable, in much the same way that .dot method chaining does with OOP methods. FYI: Haskell's reverse application operator is &.[/td]
[/tr]
[/table]

Ok so let's define some contramap operators
First off we need to define our the symbols our operators will use and the fixity (i.e. order of precedence, and associativity)
PHP:
/// Contravariant Functor
/// (>$) :: b -> f b -> f a infixl 4
infix operator >¢ : infixl4

/// Reverse Contravariant Functor
/// (>&) :: f b -> b -> f a infixl 4
infix operator >& : infixl4
Note:
In Swift we cannot use the $ symbol because it's a reserved symbol; however we can use it's alt-key equivalent; ¢; the cent symbol. We get by pressing the alt-4 keystroke.

Ok so lets add the implementation for this to our Predicate type
Here's the full code modified version of our Predicate type including implementations for both of our new contramap operators.
PHP:
public struct Predicate<A> {
  public let contains: (A) -> Bool
  
  public init(contains:  escaping (A) -> Bool) {
    self.contains = contains
  }
}

// MARK: - Contramap
extension Predicate {
  public func contramap<B>(_ f: escaping (B) -> A) -> Predicate<B> {
    return Predicate<B>(contains: f >>> self.contains)
  }
  
  public static func >¢ <B>(lhs: Predicate<A>, rhs: escaping (B) -> A) -> Predicate<B> {
    return lhs.contramap(rhs)
  }
  
  public static func >& <B>(lhs: escaping (B) -> A, rhs: Predicate<A>) -> Predicate<B> {
    return rhs.contramap(lhs)
  }
}

// MARK: - Semigroup
extension Predicate {
  func concatenate(_ with: Predicate<A>) -> Predicate<A> {
    return Predicate<A>(contains: {x in self.contains(x) && with.contains(x) })
  }
  
  public static func <> (lhs: Predicate<A>, rhs: Predicate<A>) -> Predicate<A> {
    return lhs.concatenate(rhs)
  }
}

...and finally let's adjust our previous contramap example to use these operators:

We can turn this:
PHP:
let predicates = [prefixRange("K"..."Z").contramap(^\Employee.name),
                  lessEqual(6000).contramap(^\Employee.salary),
                  greater(30).contramap(^\Employee.id)]

into this (normal contramap application operator):
PHP:
let predicates = [prefixRange("K"..."Z") >¢ ^\Employee.name,
                  lessEqual(6000) >¢ ^\Employee.salary,
                  greater(30) >¢ ^\Employee.id]

or this (reverse contramap application operator):
PHP:
let predicates = [^\Employee.name >& prefixRange("K"..."Z"),
                  ^\Employee.salary >& lessEqual(6000),
                 ^\Employee.id >& greater(30)]




Optional Information on Operator Precedence Groups (Fixity)
The infixl4 in our operator symbol declaration refers to our precedence group. The precedence groups I use for all my operators in Swift are derived from Haskell, specific this paragraph 4.4.2 on this page: https://www.haskell.org/onlinereport/decls.html

Screen Shot 2018-06-24 at 13.00.32.png

If you look at the Haskell operator signature in the comments above (or in their reference documentation); you can see that they refer to the precedence grouping that operators belong in, for example:
PHP:
/// (>$) :: b -> f b -> f a infixl 4
Here the infixl implies left associativity and the 4 is its order of precedence, better represented by the above image.

Anyway for interests sake, here are all the precedence groups I use in Swift which tie up on 1 to 1 basis with the Haskell fixities.
PHP:
precedencegroup infixl0 {
  associativity: left
  higherThan: AssignmentPrecedence
}

precedencegroup infixr0 {
  associativity: right
  higherThan: infixl0
}

precedencegroup infixl1 {
  associativity: left
  higherThan: infixr0
}

precedencegroup infixr1 {
  associativity: right
  higherThan: infixl1
}

precedencegroup infixl2 {
  associativity: left
  higherThan: infixr1
}

precedencegroup infixr2 {
  associativity: right
  higherThan: infixl2
}

precedencegroup infixl3 {
  associativity: left
  higherThan: infixr2
}

precedencegroup infixr3 {
  associativity: right
  higherThan: infixl3
}

precedencegroup infixl4 {
  associativity: left
  higherThan: infixr3
}

precedencegroup infixr4 {
  associativity: right
  higherThan: infixl4
}

precedencegroup infixl5 {
  associativity: left
  higherThan: infixr4
}

precedencegroup infixr5 {
  associativity: right
  higherThan: infixl5
}

precedencegroup infixl6 {
  associativity: left
  higherThan: infixr5
}

precedencegroup infixr6 {
  associativity: right
  higherThan: infixl6
}

precedencegroup infixl7 {
  associativity: left
  higherThan: infixr6
}

precedencegroup infixr7 {
  associativity: right
  higherThan: infixl7
}

precedencegroup infixl8 {
  associativity: left
  higherThan: infixr7
}

precedencegroup infixr8 {
  associativity: right
  higherThan: infixl8
}

precedencegroup infixl9 {
  associativity: left
  higherThan: infixr8
}

precedencegroup infixr9 {
  associativity: right
  higherThan: infixl9
}
 
Last edited:
In this thread so far we explored:
  • The difference is between covariance and contravariance
  • How to construct a type that supports contravariance including a typical implementation for contramap
  • ...and some examples.
[table="width: 90%, class: outer_border, align: center"]
[tr]
[td]Note:
We typically refer to types that provide a contramap implementation as Contravariant Functors; these conformances are quite similar to traits / interfaces / protocols i.e. it's a stipulation that any type conforming to this algebra must provide e.g. a contramap implementation.

Being an algebras and tied to mathematics; the conformance is typically a bit broader than just what methods w must implement; i.e. to truly be consider to be in conformance with the algebra we also need to validate that our implementation conforms to the rules of an algebra.

We can think of these rules as a way to test that our implementation not only behaves correctly, but that our implementation will mathematical provide the same guarantees as any other conforming implementation of that algebra. Finally just like types can conform to many different interfaces (traits / protocols); so too can our types provide conformance to multiple algebras.[/td]
[/tr]
[/table]

Next we're going explore the opposing and more familiar side of variance: covariance.

Functors
This is transformation process that we're far more accustomed to in programming; applying transformative computations to output end of a computation, for example:

PHP:
let result = square(increment(1))
Here square is covariantly applied to the output of increment.

Similarly in this example, square is applied covariantly to the iteration of each of the array's elements:
PHP:
let result = [1, 2, 3, 4].map(square) \\ [1, 4, 9, 16]

The above Array map is similar to the following iterative style code.
PHP:
var values = [1, 2, 3, 4]

// covariantly modify array
// -----------------------------------
var result = [Int]()
values.forEach { x in result.append(square(x)) }
// -----------------------------------

print(result)


The same concept of function application applies to the Optional container type i.e. we apply the function if Optional has a value, and we do nothing if it doesn't.
PHP:
let value = Int?.some(2)

// covariantly modify optional value
// -----------------------------------
var result: Int?
switch (value) {
  case .some(v):
    result = .some(square(v))
  case .none:
    result = .none
}
// -----------------------------------

print(result)

In both cases we apply a computation to the element(s) stored in a container type; and when the container is empty we simply do nothing. So whilst the internals of the array forEach process appears very different to the optional's switch; imperatively they are the same:
  • if the array is empty, we do nothing
  • if the optional is empty, we similarly do nothing

This type of process occurs frequently in code, so it can be considered unnecessary repetition and hence we should refactor this away -- which essentially can be considered as the premise for map, or more specifically it's algebra namesake, the Functor.


Functor
In mathematics, a functor is a map between categories, a function from X → Y; the rules that accompany these algebras are there to ensure consistency:

That is, functors must preserve identity morphisms and composition of morphisms.

Functor Rules:
  • f(id(x)) = x
  • f(a ∘ b) = f(a) ∘ f(b) -- for all morphisms a: X → Y and b: Y → Z

Identity
The Identity (id) function is a computation that simply returns the value that it was given.
PHP:
  public static func id<A>(_ a: A) -> A {
    return a
  }
Which means that when we map using the id function, our output should equate with the input, for example:
PHP:
  let result = [1, 2, 3, 4].map(id) \\ [1, 2, 3, 4]

Composition
In mathematics, function composition is the application of one function to the result of another to produce a third function for example:
PHP:
  let result = square(increment(2))

This means that mapping over the composition of two functions must equate with mapping over the first function followed by the mapping over the second function, for example:
PHP:
  let result = [1, 2, 3, 4].map(increment).map(square) \\ [1, 4, 9, 16]
  let result = [1, 2, 3, 4].map(increment >>> square) \\ [1, 4, 9, 16]

Typical implementation of map for an Array
PHP:
extension Array {
  public func map<B>(_ f: (Element) -> B) -> [B] {
    var result = [B]()
    self.forEach { x in result.append(f(x)) }
    return result
  }
}

Typical implementation of map for an Optional
PHP:
extension Optional {
  public func map<B>(_ f: (Element) -> B) -> B? {
    switch (self) {
      case .some(value):
        return .some(f(value))
      case .none:
        return .none
    }
  }
}

These two different implementations of map for Array and Optional are both compliant with the rules defined for Functor, and hence we can use map to typically replace any block of code that imperatively applies a function to the elements in a container type like Array, Optional, Either, and many others, as long as these types similarly are in compliance with the rules for Functor.
 
Last edited:
In this post we going to exploring the concept of map in relation to reduce(fold); more specifically how we can customise our mapping processes to not only transform elements, but also to ultimately at the same time reduce these elements with an associative binary operation e.g. addition, multiplication, ...


Semigroups and Monoids
In one of the previous posts in this thread I've briefly touched on the concept of Semigroups; here's a quick recap.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Note: The <> operator is derived from the algebraic term Semigroup; which is an algebraic structure with an associative binary operation e.g. addition, multiplication, logical conjunction, etc.
[/td]
[/tr]
[/table]
An algebra that is inherits Semigroups is the Monoid; i.e. types with an associative binary operation that has an identity element. Basically the only difference between a Semigroup and Monoid, is that the Monoid also requires an identity element.

Let's look at a few examples for more clarity:

Addition
PHP:
// When we perform addition on Integers, the only value (element) that  
// we can add that won't affect the result is zero
1 + 2 + 3 + (0) == 1 + 2 + 3

// Which makes zero (0) the identity element for addition of Integers

Multiplication
PHP:
// When we perform multiplication on Integers, the only value (element) 
// that we can multiply with that won't affect the result is one
1 * 2 * 3 * (1) == 1 * 2 * 3

// Which makes one (1) the identity element for multiplication of Integers

Identity and Reduction
This concept of an identity element is important when we want to for example reduce an Array of Integers by adding the values together i.e. to produce the sum of the array

To make this even more clear; let's look at an example of some imperative code to sum an Array of Integers.
PHP:
let input = [1, 2, 3, 4, 5]
var sum = 0
input.forEach { sum += $0 }
print(sum) \\ 15
In the above code we defined a variable called sum to hold the result of our iterative addition of array values. This sum variable had to be assigned an initial value; which in this case is zero (0), because only zero would not affect outcome when we are adding Integers.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]This initial value for our sum is what is mathematically referred to as an identity.[/td]
[/tr]
[/table]

Reduce
Similar to the map method, reduce is a method that allows us to provide as its arguments, an identity, and a function that performs a binary associative operation on the elements of the input container type.

Let's as example, look at how we can use the reduce method to sum an array of integer values:
PHP:
let input = [1, 2, 3, 4, 5]
let sum = input.reduce(0, +)
print(sum) // 15
This is a very terse example; that requires a bit of explanation to understand it. The reduce method is .dot chained from our input array; the first parameter we provide is zero; because only zero will have a neutral effect on our sum i.e. its our identity for addition. Secondly the + refers to the operator for addition; which takes two values and adds them together. Which in this case is accumulating sum and the next element value. Swift's syntactic sugar allows us to in point-free style avoid the need to explicitly specify the parameters. To make this even more clear, let's look at an expanded version of this:
PHP:
let input = [1, 2, 3, 4, 5]
let sum = input.reduce(0, { (sum: Int, element: Int) in sum + element })
print(sum) // 15
Both of these bits of code are the same to compiler; except that in the first very terse example the compiler was able to extrapolate for the blanks i.e. it didn't need to be told that our reduction function worked with Int values because that can determined from both our input array and our zero identity value.

Reduction over a input container type (like array) is certainly not limited to always return a single value
We can if we wanted to use our reduce method to effect the same change that our map function performed, for example, let's increment our input values as opposed to summing them using reduce:

PHP:
let input = [1, 2, 3, 4, 5]
let result = input.reduce([], { $0 + [increment($1)] })
print(result) // [2, 3, 4, 5, 6]
In this example we provide an empty array [] as our identity, secondly we took accumulation ($0); previously this was the sum variable, and added to it an a single value array of our element ($1) with the increment function applied.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Note: in the above code we have provided an empty array [] as our initial value; because adding an empty array [] to any array has the same result as zero for addition and one for multiplication. i.e. an empty array is the identity value for reduce operations on an array[/td]
[/tr]
[/table]

To again hopefully make this a bit more clear, let's see an equivalent expanded form of this:
PHP:
let input = [1, 2, 3, 4, 5]
let result = input.reduce([], { (accum: [Int], element: Int) in accum + [increment(element)] })
print(result) // [2, 3, 4, 5, 6]
Basically we have a closure (or function), that takes in a accum parameter; our running "sum total" of the reduction, and a element i.e. the next value iteratively retrieved from our container type. The plus operator in Swift works the same as the .append method; adding the single element array containing our incremented element to our accumulation.


Oh wait, but isn't that similar to map?

For example: the above reduce is doing the same as this map code?
PHP:
let input = [1, 2, 3, 4, 5]
let result = input.map(increment)
print(result) // [2, 3, 4, 5, 6]

Which means that reduce can perform exactly the same operation as map.

The reason for this resides in the relationship between Functor (map's algebra) and Monoid (reduce's algebra). Functor essentially inherits from Monoid, and is considered a Monoid Homomorphism -- which is a mathematical mouthful that simply means:
  • Monoid: we perform a binary associative operation with an identity, for example: addition
  • Homomorphism; implies that we perform a computation that preserves the structure. For example: if our input was an array, then the output must also be an Array.
Which by definition only makes map a homomorphism, and not reduce, because reduce can for example: perform an operation that would convert an array into a single output value i.e. the structure of the input array would not preserved.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]So even though reduce can perform the same operation as map; it algebraically does not provide the same guarantees that map does.[/td]
[/tr]
[/table]

In the next post we'll explore this ability of reduce to do other operations a little more deeply; because this ability is not only a nicety -- it can in a few instances be used to optimise away unnecessary computation e.g. it can in some cases provide a composable mechanism to turn a quadratic operation O(n²) into a linear operation O(n).
 
Last edited:
Reducers and Transducers

Let's now expand on the concept of monadic reduction of elements in a container, or more specifically the signature form of these functions that we use with the reduce function:
PHP:
(C, A) -> C
This is the signature form of a reducer, and is used in a wide variety of contexts, for example:
  • Redux
  • Elm
  • Flux
  • ...
In these this reducer signature form is more commonly represented as:
PHP:
(state, action) => state
Which is the same form as our generic signature (C, A) -> C. Another simplistic way to think of a reducer is as an accumulation of elements, as in the summing of Integers; the C in this case represents the sum variable that stores the accumulating Integer sum as we iterate over the Integer values stored in the input Array. In this general sense the State/Action form is no different as State is simply the accumulation of a set of Actions.

For clarity's sake lets go over two simple examples:
PHP:
// Example 1: Sum Reducer on an Integer Array
func sum(_ accum: Int, _ element: Int) -> Int {
  return accum + element
}

let input = [1, 2, 3, 4, 5]
let total = input.reduce(0, sum)
print(total) // 15

// Example 2: Increment Reducer for an Integer Array (similar to map)
func incr(_ accum: [Int], _ element: Int) -> [Int] {
  return accum + [element + 1]
}

let result = input.reduce([], incr)
print(result) // [2, 3, 4, 5, 6]
In the above code, both the sum and incr functions are reducers

PHP:
let result = [1, 2, 3].map(square).reduce(0, sum) // 14
The above code is a reasonable way to sum the square of the elements in the array; what it is not is efficient, because we iterate twice over the array for our result.

We can only fix this in the reducer, because map being a homomorphism cannot reduce an array to the sum of its elements, but a reducer can perform the same morphism as map.

PHP:
let result = [1, 2, 3].reduce(0, { a, e in sum(a, square(e)) }) // 14
Does something look a little familiar about this? We are adjusting the input to the add reducer; yes it's contravariance.

This however doesn't compose simply, consider for example the following code:
PHP:
func isEven(_ x: Int) -> Bool {
  return x % 2 == 0
}
let result = [1, 2, 3, 4, 5, 6].filter(isEven).map(square).reduce(0, sum) // 56
Ok so now lets optimise this with a single reduce.
PHP:
let result = [1, 2, 3, 4, 5, 6].reduce(0, { a, e in isEven(e) ? sum(a, square(e)) : a }) // 56
This is not easy to read like the .dot method chaining and clearly won't scale, largely because of a lack of consistency in how we contravariantly applied the effect to the input, and again like many non FP banes it simply doesn't doesn't compose easily.

The only win at this stage is performance; the .dot method chaining version iterates over array elements three times, where our contravariant reducer version does it in one iteration.

Introducing Transducers
Transducers are composable algorithmic transformations, that are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element.

The signature pattern for a transducer is:
PHP:
((C, A) -> C) -> ((C, B) -> C)

|___________|     |__________|
      1                 2
As you can see the 1st part of the signature; it's exactly like our reducer, and that would be correct, because a transducer takes a reducer as its only input parameter. Now look at the 2nd part of this signature, the output; it's almost exactly the same as the 1st part except the A has become a B; i.e. our input element value was transformed by the internals of our transducer.

This essentially makes transducers a pipeline component that enables contravariant changes to the input of a reducer. i.e. a transducer being decoupled higher order function on its own does nothing -- but when used in conjunction with a reducer and a type that supports a monoidal reduce; it becomes a tool that we can use to apply more complex transformations to our reducers, without any significant performance and / or processing cost incurred.

[table="width: 90%, class: outer_border, align: center"]
[tr]
[td]A transducer is a higher order function that takes a reducer with an element input of A and returns a reducer with an element input of B

...hello contravariance my "old" friend![/td]
[/tr]
[/table]


In the next post we'll examine how we create these transducers, how they compose and finally how to use them in conjunction with our reducers.
 
Last edited:
[)roi(];21741795 said:
This means that mapping over the composition of two functions must equate with mapping over the first function followed by the mapping over the second function, for example:
PHP:
  let result = [1, 2, 3, 4].map(increment).map(square) \\ [1, 4, 9, 16]
  let result = [1, 2, 3, 4].map(increment >>> square) \\ [1, 4, 9, 16]

Is this not the function increment and not the variable? Wouldn't the increment function require a value, which would increment each of the original values which will then be squared by the following map?
Would you rather use .map(inc).map(square) or >>> instead for the functor chaining?

I liked this resource after Googling a bit after reading yours: https://www.swiftbysundell.com/posts/composing-types-in-swift and http://www.mokacoding.com/blog/functor-applicative-monads-in-pictures/

So use map/Functor whenever input = output type, if converting it doesn't apply, use reduce. If you really need performance, you can combine reduce and map if needed to one reduce.

This is not easy to read like the .dot method chaining and clearly won't scale, largely because of a lack of consistency in how we contravariantly applied the effect to the input, and again like many non FP banes it simply doesn't doesn't compose easily.
By saying it won't scale, I assume you mean if we want to add more functions. Wouldn't this scale better with more calls?

Still waiting on that next post. :D
On a side note, I am at the halfway point of that course, been quite busy lately.
 
Is this not the function increment and not the variable? Wouldn't the increment function require a value, which would increment each of the original values which will then be squared by the following map?
Would you rather use .map(inc).map(square) or >>> instead for the functor chaining?
Functors laws are as follows:
  • input.map(id) == input
  • input.map(f).map(g) == input.map(f >>> g)
id is a function that returns whatever value you give it unchanged; hence mapping an array of Integers over id, will return an array of Integers that equates with the original input.
The >>> is of course the forward composition operator, so
PHP:
let input = [1, 2, 3, 4]
input.map(increment >>> square) // [4, 9, 16, 25]

// is exactly the same as writing this...
input.map({ x in square(increment(x)) }) // [4, 9, 16, 25]

// for your review here is the definition of the >>> operator
public func >>> <A, B, C>(f: escaping (A) -> B, g: escaping (B) -> C) -> (A) -> C {
  return { a in g(f(a)) }
}

As for the ability to call a function in what's termed a point-free (tacit) style, this is just the ability to obviate code that can be inferred, for example:
PHP:
input.map({ x in increment(x) }) \\ [2, 3, 4, 5]
The x variable in above code serves only 1 purpose; that is to explicitly nominate to the compiler the variable we want the map method to relay its iterated elements through. i.e. maps passes 1 to x and that x is then used as the input value for the increment function.

Tacit or point-free style this extra detailing of what's referred to as a point; x is a point in the above example and is unnecessary because the compiler can easily infer this for us, because it knows the parameters of both map and increment; hence in a point-free style we prefer:
PHP:
input.map(increment) // i.e. we removed the unnecessary x point

Going back to the composition of increment and square, we can do the same:
PHP:
// If we wanted to be very explicit we could write the mapping over 1st incrementing a value and the 2nd squaring it as follows:
let incrementAndSquare = { x in square(increment(x) }
input.map({ x in incrementAndSquare(x) }) // [4, 9, 16, 25]
Now compares this to the point-free version that does the same thing:
PHP:
input.map(increment >>> square) // [4, 9, 16, 25]
i.e. its far more tacit code; not only have we removed the explicit x points, we have also remove the need to create a new function prior to our map. The higher order of operator precedence for the >>> composition operator versus function application is what ensures that map only see the result of a composition of increment and square.

Sundell spends a lot of time sharing knowledge, and his stuff is good; so good choice.
Mokacoding has done a good job of translating of the original Haskell article; good find.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]When it comes to FP and its abstract algebras (code patterns); the best advice I could give is to remember that to truly understand any concept or approach requires an understanding of where to use it; more typically what are its pros/cons.[/td]
[/tr]
[/table]

So use map/Functor whenever input = output type, if converting it doesn't apply, use reduce. If you really need performance, you can combine reduce and map if needed to one reduce.
Map is simply a transformation of elements in a container, without changing the container. i.e. mapping over the elements in an Array, only transform the elements and not the Array. In short Array in and Array out.

Reduce implies a change, so our container can be affected in contrast with map i.e. we can reduce over an Array of elements; adding the together to produce a single element. Similarly we can reduce over an Array of elements whilst preserving the Array to simply apply a transformation to the elements exactly the same way map does.

Which means we could rewrite the internals of the map function using reduce, the same concept btw carries forward to any operation you can perform on a container type like Array i.e. we can rewrite the internal compute of filter as a reduce, etc...

By saying it won't scale, I assume you mean if we want to add more functions. Wouldn't this scale better with more calls?
Ok this statement leads on from comparing methods to functions; so let's define a few to help with clarity:

PHP:
// let's define a few extension methods on Integer
// `self` refers to the input Integer on which we would .dot chained this method
extension Int {
  func increment() -> Int {
    return self  + 1  
  }

  func square() -> Int {
    return self * self
  }
}
Ok so now let's use them. Let's say we want to increment an Integer value and square the result.
PHP:
2.increment().square()  // 9
That's quite simple, 2 is incremented by the increment method and the result of that is squared.

Ok, now let's try to do this for an Array of numbers
PHP:
let input = [1, 2, 3, 4]
let output = input.map({ x in x.increment().square() })
print(output)  // [4, 9, 16, 25]
Great that was possible. Now for a challenge, let's convert the result of the increment and square to a String
PHP:
let output = input.map({ x in String(x.increment().square()) }) 
print(output) // ["4", "9", "16", "25"]
As you can see this is becoming quite busy, and with more complex computations this would even be worse.

Now imagine a scenario where you have a computation that is the composition of a number of methods and we use it in a number of different parts of our codebase; hence following the DRY (don not repeat yourself) advice, we should encapsulate this as a separate method that we can reuse, for example:
PHP:
extension Int {
  func incrementAndSquareToString() -> String {
    return String(self.increment().square())
  }
}
Great so we can now use this in place of the { x in String(x.increment().square()) }:
PHP:
let output = input.map({x in x.incrementAndSquareToString() })
print(output) // ["4", "9", "16", "25"]

Now let's look at the full FP and point-free version of this.
PHP:
let output = input.map(increment >>> square >>> String.init)
print(output) // ["4", "9", "16", "25"]

...and let's compare that to the full method version:
PHP:
extension Int {
  func incrementAndSquareToString() -> String {
    return String(self.increment().square())
  }
}

let output = input.map({ x in x.incrementAndSquareToString() })
print(output)
You should see that the FP version requires less code. i.e. we didn't need this:
PHP:
  func incrementAndSquareToString() -> String {
    return String(self.increment().square())
  }
...and our map code was also point-free.

Finally if you're still not convinced consider what we would have to do to create another composition of increment and square, but instead of converting it to String we want to convert it to a Double.

With methods we would do it as follows:
PHP:
extension Int {
  func incrementAndSquareToDouble() -> Double {
    return Double(self.increment().square())
  }
}

let output = input.map({ x in x.incrementAndSquareToDouble() })
print(output) [4.0, 9.0, 16.0, 25.0]
As you can see we had to add another 3 lines of code, with this FP version it's as simple as this:
PHP:
let output = input.map(increment >>> square >>> Double.init)
print(output) [4.0, 9.0, 16.0, 25.0]

i.e. no extra lines of code. Whilst these are very simple examples; this compounds throughout a codebase. The point is that methods simply do not compose, whereas functions do. Hence I'd argue that in order to truly follow the advice of favouring composition over inheritance, you need to consider using more functions rather methods in your codebase.

This is also just a very small part of FP; but this notion of FP code being far more terse (less code) than OOP keeps repeating. The wins are that we create code built up using a composition of small functions; and with each function being pure testing becomes easy.

Testing methods isn't easy; hence it requires leaning on constructions like dependency injection and mocks; which is a whole other story -- because it's debatable about what is being tested and what is not. In FP we simply try to make as many things pure functions and the push the mutability more typical of OOP types to the edges of the codebase i.e. affect state only when absolutely necessary.

Still waiting on that next post. :D
On a side note, I am at the halfway point of that course, been quite busy lately.
Yeah, sorry about that... it's been a busy weekend and week; but I had set aside time this weekend to wrap up this thread.

As for the roadmap; I'm trying to fill in some of the blanks (or prior knowledge) that we need to look at a complete program. Have been considering the idea that an interesting endpoint of this could be a small game; something not too complicated like recreating the original pong game with a FP codebase -- what do you think?.
 
Last edited:
Thanks for the explanation, makes quite a bit of sense to me.

Don't worry about taking time, I'm freeloading so I have no rights to force you to even create the threads that you have already done, not to mention to force you to continue. :)

I'm for it, haven't created a game like that in mostly OOP style yet. Would be quite interested in it.
 
Thanks for the explanation, makes quite a bit of sense to me.

Don't worry about taking time, I'm freeloading so I have no rights to force you to even create the threads that you have already done, not to mention to force you to continue. :)

I'm for it, haven't created a game like that in mostly OOP style yet. Would be quite interested in it.
No worries... it has to be wrapped up anyway.

Yeah I thought a game would be a good approach; because we can abstract out all the parts of game engine including game state, from the UI view and controllers. i.e. to have something that could quite easily share the engine and state between disparate UIs, web, CLI, or even mobile devices employing touch and/or accelerometer. Naturally the focus the would be more on building the engine, and then picking 1 or 2 UIs (at max) to demonstrate this with.
 
Transducers
Let's recap from the last post:

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Transducers are composable algorithmic transformations, that are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element.

The signature pattern for a transducer is:
PHP:
((C, A) -> C) -> ((C, B) -> C)
The above signature is generic form of a function that takes a reducer and returns a reducer:
  • C refers to the accumulator, or the base state
  • A refers to the element, or the action we apply to the state.
  • B is the result of the contravariant computation on A, or the contravariant change we effect on our action.
[/td]
[/tr]
[/table]

So the transducer is a higher order function takes an accumulator and an element, and returns a new accumulator. With reducers represented as State / Action computations, the function would take a State and an Action, apply the Action to the state and then return this new State.

In the last post we reasoned that its possible to replicate any computation on a container type like an Array as a reducer using Contravariance, for example the following .dot chain computation:
PHP:
func isEven(_ x: Int) -> Bool {
  return x & 1 == 0
}
let result = [1, 2, 3, 4, 5, 6].filter(isEven).map(square).reduce(0, +) // 56

Can be rewritten as:
PHP:
let result = [1, 2, 3, 4, 5, 6].reduce(0, { a, e in isEven(e) ? a + square(e) : a }) // 56
While this achieves the same computation with a single reduce it as mentioned before doesn't scale so easily; just think about multiple transformations, maps, flatmaps, logical disjunctions (OR), etc. The computation won't be as easy to code and the resulting complexity would likely be difficult to be maintain.

This is where transducers can help.
Let's look at how we would sum the square of the integer values in an array:
PHP:
let input = Array(0...10) // [0,1,2,3,4,5,6,7,8,9,10]
let result = input.map(square).reduce(0, +)
print(result) // 385
The above .dot chain approach we first square the values using map and then reduce the output of the square with the + operator (the 0 is the identity value for addition i.e. we initialise our sum accumulator with zero because it won't affect the result). This is fairly easy to understand as it captures the essence of what we want to compute without exposing the internal steps of how this is accomplished; declarative programming.


The only potential negative aspect of that code relates to how it is computed, for example:
  • map typically iterates over the elements in array i.e. O(n) complexity
  • reduce similarly iterates over every element in the resulting array produced by map i.e. O(n) complexity
...or we iterate over every element twice.

[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Note:This btw won't apply when these computations are lazy; which is exactly what transducers also enable i.e. an inherent laziness due to the contravariant composition of computations affecting the input.[/td]
[/tr]
[/table]

Ok so let's now recreate the computation of the sum of the squares of the inputs using a transducer:
First off let's create a transducer that squares it input elements. C is the generic placeholder for the accumulator (or state), and for the element we have specified it's an Int .
PHP:
func contraSquare<C>(_ reducer: escaping (C, Int) -> C) -> (C, Int) -> C {
  return { a, e in return reducer(a, e * e) } // here we simply multiply element by itself
}

let result = input.reduce(0, (+) |> contraSquare)
print(result) // 385
We defined a transducer function called contraSquare that takes a reducer as it's input and returns a reducer as it's output; internally we square the element input value i.e. a contravariant change.

The use of this may look a bit strange; but let's walk it through. Our input (array of integer values) is .dot chained to reduce with a identity value of 0 same as before, but there is no map preceding this. After the 0identity, we encapsulate the + operator is parenthesis (), which simply tells the compiler that we want to prioritise the determine of its signature on its own; which is a just a way to making things a bit easier for the compiler to figure our what we want to do. Then we use our |> pipe forward operator, which is if you remember is just a reversal of normal function application i.e. it simply changes the order that we write function application, without it, the code would look like this:
PHP:
result = input.reduce(0, contraSquare(+))
As to why this is important, will become apparent soon.

Limitations of this contraSquare transducer
Created a unique transducer for every computation is not only impractical, it also means we cannot easily translate out .dot chaining map computations to their transducers equivalents. To solve this we really need a contramap that will create a transducer for us, for example:
PHP:
func contramap<A, B, C>(_ f:  escaping (A) -> B) ->  escaping Reducer<C, B>) -> Reducer<C, A> {
  return { reducer in { c, a in reducer(c, f(a)) } } // here we reduce over accumulator (C), with result of function f applied to input (A)
}
This contramap functions take a closure as it's input (where we specify how we want to transform our element), and then we return a transducer function that takes a reducer as its input and returns a reducer as its output.

Let's recreate the sum of the squares of the input values using contramap:
PHP:
let result = input.reduce(0, (+) |> contramap(square))
print(result) // 385
The above contramap now works similar to its covariant .dot chaining map.

Let's now define transducers for both flatMap and filter:
PHP:
func contraflatMap<A, B, C>(_ f: escaping (A) -> [B]) ->  escaping Reducer<C, B>) -> Reducer<C, A> {
  return { reducer in { c, a in f(a).reduce(c, reducer) } } // f takes an element, applies a transform & returns a wrapped result
}

func contrafilter<C, A>(_ predicate: escaping (A) -> Bool) ->  escaping Reducer<C, A>) -> Reducer<C, A> {
  return { reducer in { c, a in predicate(a) ? reducer(c, a) : c } } // the predicate determines if we reduce over that element, or ignore it by simply returning the previous accumulation (state).
}

Let's have a look at an example that uses all three of these higher order transducers:
flatMap as the name implies performs not only a map transformation, but also flattens the input values, so demonstrate this let's add another array dimension to our input values i.e. input2 is a 2D array
PHP:
let input2 = input.map({ [$0] }) // [[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]]

// rudimentary determination of prime values (created for clarity instead of speed)
func isPrime(_ candidate: Int) -> Bool { 
  switch candidate {
  case 0, 1: return false
  case 2, 3: return true
  default:
    let ceiling = Int(Double(candidate).squareRoot()) // we only need to check up to square root of the candidate
    for i in 2...ceiling where candidate % i == 0 { return false } // if its divides cleanly (no remainder) then it's not a prime
  }
  return true
}

// here's an example using .dot chaining
// we first flatmap over input2 with a computation that simple returns the value it was given
// flatMap then flattens this, which in this case means it turns a 2D array into a 1D array 
let dotresult = input2
  .flatMap({ $0 })
  .map(increment) // here we simply increment the array elements by 1
  .filter(isPrime) // now we filter only primes.
  .reduce(0, +)
print(dotresult)  // 28

// note: this is equivalent transducer version of this; except the order runs in reverse
// see explanation below.
let contraresult = input2.reduce(0, (+)
    |> contrafilter(isPrime)
    |> contramap(increment)
    |> contraflatMap({ $0 })
)
print(contraresult) // 28
In the above .dot chaining example we iterate over our:
  • 2D array first to flatten the values
  • we then iterate over the result of that and increment each values
  • we then again iterate over the result of increment to filter out only the primes
  • finally we iterate of the prime results to reduce them to a sum of the primes.
Basically 4 iterations (not counting the internal computation of isPrime).

In comparison the transducer version does this with 1 iteration i.e. contravariantly applying the computation to each input element. Note that the order of the contraflatMap, contramap and contrafilter and reduce run in the opposite order to how it is with the .dot chaining version. the reason for this is due to the fact that we are applying the computation to the input as opposed to the output, so the order runs in reverse.
 
Last edited:
Continued...

Ok but why do we need to use the |> pipe forward operator?
This is not so much a need to use it as a preference to use it because it makes it far easier to read the code, and far easier to comment out parts of the contravariant computation. Let's start off by looking an equivalent contravariant computation of the sum of the primes in the input2 array:
PHP:
let contraresult2 = input2.reduce(0, contraflatMap({ $0 })(contramap(increment)(contrafilter(isPrime)(+))))
print(contraresult2) // 28
Surely you can agree that quite confusing and its more difficult to disable e.g. contrafilter computation, with the |> pipe forward operator version, we can simply do this to temporarily disable the filter in the same it would work with the .dot method chaining version, for example:
PHP:
  let dotresult = input2
    .flatMap({ $0 })
    .map(increment)
//    .filter(isPrime)
    .reduce(0, +)
  print(dotresult) // 66
//}

//measure("reducer / transducer") {
let contraresult = input2.reduce(0, (+)
//  |> contrafilter(isPrime)
  |> contramap(increment)
  |> contraflatMap({ $0 })
)
print(contraresult) // 66
So hopefully you can now appreciate why we prefer using the operator operator with this, because it provides all of the flexibility of the .dot method chaining version whilst performing the computation in a single iteration.

Naturally many standard libraries have built in alternative mechanisms for lazy evaluation that similarly delay and compose computations until its required, for example in Swift we can using this form of the .dot method chaining to lazily evaluate the sum of the primes in our input2 array:
PHP:
  let dotresult = input2.lazy // we simply add the keyword lazy to iterate only once.
    .flatMap({ $0 })
    .map(increment)
    .filter(isPrime)
    .reduce(0, +)
  print(dotresult) // 28
[table="width: 80%, class: outer_border, align: center"]
[tr]
[td]Note:
The contravariant version is by default lazy, because we are also compositing our transformations, except we are making these adjustments to the input of our reducer.
As for the reducer; this is a simple concept; essentially most binary operators can be consider to be a reducer, for example:
PHP:
// + operator takes a value on left and adds to a value on right, and returns the sum of the two.

let sum = 1 + 2
// Think of 1 as being the State, and 2 as being with Action or effect we are making to our State (1)
// At the end of this reducer's computation, our State is now a 3; which is the result of adding the Action (2) to our original State (1)
[/td]
[/tr]
[/table]



So then why do we even need to know about all this transducer and reducer stuff?
Quite simply because not all aspects of the a language standard library is optimised to do this, and your custom collection types would most certainly not do this by default, and further more when we finally get around to building our first functional application (a game engine), we will be using a State/Action structure together with reducers and transducer to apply Effects (actions) to our State structure. Plus this is used quite commonly in frameworks like Redux, Flow, Functional Reactive Programming, etc. ...and we I thought it would be a good way to show you another use of contravariance and how it juxtaposes with covariant computations.

...and that concludes this thread.

Note: for languages that don't support custom operators, we can achieve something quite similar with the .dot chaining of contravariant methods. As example in Java or C# we can achieve this though Function types and / or extension methods; or we just fall back on the standard method application.
 
Last edited:
Top
Sign up to the MyBroadband newsletter
X