Functional Programming

thanks guys, I'll certainly check out the coursera course and youtube links above.

To be honest, I'm not a "trained" developer per se ... I've ended up developing as a consequence of studies involving computations. Seeing terms like transformations, monoids and the like being used kinda excite me :D

Actually, I'm working on a project which will implement some form of parallelism; from what I read online FP seems great at it (sometimes a little too great for that matter!). So I'm quite lucky this thread came along just as I'm about to begin creating my model, alhtough I obviously won't be able to use FP well any time soon! :p
 
thanks guys, I'll certainly check out the coursera course and youtube links above.

To be honest, I'm not a "trained" developer per se ... I've ended up developing as a consequence of studies involving computations. Seeing terms like transformations, monoids and the like being used kinda excite me :D

Actually, I'm working on a project which will implement some form of parallelism; from what I read online FP seems great at it (sometimes a little too great for that matter!). So I'm quite lucky this thread came along just as I'm about to begin creating my model, alhtough I obviously won't be able to use FP well any time soon! :p
Only a pleasure. I'll be adding more articles in time; well as soon as I figure a good way to convey some of the more complex topics; plus writing this down helps me with my comprehension -- you must understand something before you can explain it.

Parallelism.
Yes FP and parallelism make a perfect marriage, specifically because they avoid identity and side-effects (the OOP magic). Whilst those OOP attributes work very well in certain circumstances (UI Events), they're absolutely awful when it comes to multi-threading, because when sharing a single object reference you are always going to be forced to know what the other parallel processes are doing i.e. you need a way to monitor or to communicate. For example: 1 thread ends up waiting for another to release a resource they require.

In the world of immutability; everyone takes a immutable copy of what they need and process crunch until they're finished. i.e. no synchronisation is ever needed between threads, meaning we maximise throughput.

FP is a large and complex subject; probably because it started in the 1930s i.e. for a lot of us newcomers (incl. me), we've got ~80 years of knowledge to absorb... but don't let that scare you. Like learning to touch type or learning to play a musical instrument; it's difficult and takes time to master, but it's absolutely pleasurable once you have.

Plus the path is clear; all languages are actively adopting FP, so it's part of our future.
 
Last edited:
Article 3: C# Code

Standard procedural process to apply a transform.
Code:
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var mutable = new List<int>();
foreach (int value in numbers) {
  var newNumber = value + 1;
  mutable.Add(newNumber);
}
var numbersPlusOne = mutable.ToArray();

Functional equivalent using C# Linq; if you remember I said many of C# FP enhancements fall under the banner of Linq.
Where Microsoft has chosen to differ from the norm is in the syntax i.e. they've chosen a syntax that aligns more closely with Structured Query Language (SQL).
Code:
using System;
using System.Linq;
using System.Collections.Generic;

namespace Functional
{
  class MainClass
  {
    public static void Main(string[] args)
    {
      var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      var numbersPlusOne = from element in numbers
                           select element + 1;
    }
  }
}

Here we abstract away the transform to a reusable function that can be applied to all Integers, the same can apply to any type. Extensions overall are concise way to partition your code for reusability.
Code:
namespace MyExtensions
{
  public static class IntExtension
  {
    public static int incrementBy(this int intValue, int amount)
    {
      return intValue + amount;
    }
  }
}

using System;
using System.Linq;
using System.Collections.Generic;
using MyExtensions;

namespace Functional
{
  class MainClass
  {
    public static void Main(string[] args)
    {
      var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
      var numbersPlusOne = from element in numbers
                           select element.incrementBy(1);
    }
  }
}

Real World - Example 1
Code:
using System;
using System.Linq;
using System.Collections.Generic;

namespace Functional
{
  public struct User
  {
    public String name;
    public String email;

    public User(String name, String email)
    {
      this.name = name;
      this.email = email;
    }
  }

  class MainClass
  {
    static void SendCorrespondence(IEnumerable<string> emails)
    {
      // do something
    }

    public static void Main(string[] args)
    {
      var mcdonald = new User("Ronald Mcdonald", "[email protected]");
      var sanders = new User("Colonel Sanders", "[email protected]");
      var users = new User[]{ mcdonald, sanders };
      var emails = from user in users
                   select user.email;
      SendCorrespondence(emails);
    }
  }
}

Real World - Example 2
Code:
namespace Functional
{
  public struct User
  {
    public String name;
    public String email;

    public User(String name, String email)
    {
      this.name = name;
      this.email = email;
    }
  }

  class MainClass
  {
    static void SendSales(IEnumerable<string> emails)
    {
      // do something
    }

    public static void Main(string[] args)
    {
      var mcdonald = new User(name: "Ronald Mcdonald", email: "[email protected]");
      var sanders = new User(name: "Colonel Sanders", email: "[email protected]");
      var nandos = new User(name: "Barci Nandos", email: "");
      var users = new User[]{ mcdonald, sanders, nandos};
      var emails = from user in users
                   where user.email.Length == 0
                   select user.email;
      SendSales(emails);
    }
  }
}

Real World - Example 3
Option type, or in the case of C#; the Nullable type, work a little differently in C#, you can only flag value types like (int, float) with the '?' attribute, class types like (String) being reference types are Nullable by design. So yes, this is a bit strange compared to Swift, but in practice you can achieve something similar (but not really the same in behaviour i.e. the compiler doesn't protect you against Null pointer exceptions).
Code:
using System;
using System.Linq;
using System.Collections.Generic;

namespace Functional
{
  public struct User
  {
    public String name;
    public String email;

    public User(String name, String email = null)
    {
      this.name = name;
      this.email = email;
    }
  }

  class MainClass
  {
    static void SendSales(IEnumerable<string> emails)
    {
      // do something
    }

    public static void Main(string[] args)
    {
      var mcdonald = new User(name: "Ronald Mcdonald", email: "[email protected]");
      var sanders = new User(name: "Colonel Sanders", email: "[email protected]");
      var nandos = new User(name: "Barci Nandos");
      var users = new User[]{ mcdonald, sanders, nandos};
      var emails = from user in users
                   where user.email == null
                   select user.email;
      SendSales(emails);
    }
  }
}

Flatmap - Example 4
In Linq you can typically use 'SelectMany' to flatten & transform arrays (like flatmap), but in C# you have to be aware of how the 2D array was defined, For example:
  • If you defined the 2D array using the multi-dimensional construct [,] then you're going to struggle to get `SelectMany` to work with it.
  • However if you defined your 2D array using the jagged array construct [][], then the conversion is quite simple re it easily converts to IEnumerable<IEnumerable<T>> which is what Linq uses.
Anyway I am including two solutions to this problem due to the behaviour differences between the two. Pick your poison.
Code:
using System;
using System.Linq;
using System.Collections.Generic;

namespace Functional
{
  class MainClass
  {
    // Used to Flatten Multidimensional Array to IEnumerable<T>
    public static IEnumerable<T> Flatten<T>(T[,] map)
    {
      foreach (var row in map) {
        yield return row;
      }
    }

    public static void Main(string[] args)
    {
      // 2D Multidimensional Array
      var apartments1 = new String[,] { 
        { "jack", "betty"},
        { "john", "nancy"},
        { "peter", "stacey"}
      };
      var occupants1 = Flatten(apartments1);

      foreach (String occupant in occupants1) {
        Console.WriteLine(occupant);
      }

      // 2D Jagged Array
      var apartments2 = new String[][] {
        new String[] { "jack", "betty"},
        new String[] { "john", "nancy"},
        new String[] { "peter", "stacey"}
      };
      String[] occupants2 = apartments2.SelectMany(x => x).ToArray();

      foreach (String occupant in occupants2) {
        Console.WriteLine(occupant);
      }
  }
}
...and that's it for C#, hopefully I covered it all... Next up Java...
 
Last edited:
MapReduce.jpg
Map & Reduce in sandwich making. i.e. Map transforms the ingredients, and Reduce aggregates the ingredients to make a sandwich.
 
Article 3: Java Code

Standard procedural process to apply a transform.
A bit of explanation is need; standard int array is immutable re it's a primitive type. Hence we need to use ArrayList<Integer> as our immutable array, moving through a different type naturally is going to incur some penalties, most especially on the way back, where I needed to create method to convert ArrayList<Integer> back to a primitive int array. As you'll see the FP Higher-order function approach is significantly simpler (as we should expect from FP code).
Code:
import java.util.ArrayList;
import java.util.Iterator;

public class main 
{ 
  public static int[] convertToPrimitiveArray(ArrayList<Integer> integers)
  {
    int[] ret = new int[integers.size()];
    Iterator<Integer> iterator = integers.iterator();
    for (int i = 0; i < ret.length; i++)
    {
      ret[i] = iterator.next().intValue();
    }
    return ret;
  }

  public static void main(String[] args) 
  {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    ArrayList<Integer> mutable = new ArrayList<Integer>();
    for (int value : numbers) {
      int newNumber = value + 1;
      mutable.add(newNumber);
    }
    int[] numbersPlusOne = convertToPrimitiveArray(mutable);
  }
}

Functional equivalent; as you see Java 8 is quite consistent with general use and syntax.

Code:
import java.util.stream.IntStream;

public class main { 

  public static void main(String[] args) 
  {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] numbersPlusOne = IntStream.of(numbers)
            .map((value) -> value + 1 )
            .toArray();
  }
}


Here we abstract away the transform to a reusable function; Java 8 unlike Swift and C# cannot extend System types, the same can apply to any type. Extensions overall are concise way to partition your code for reusability.
Code:
import java.util.stream.IntStream;

public class main { 

  static int increment(int value, int amount)
  {
    return value + amount;
  }

  public static void main(String[] args) 
  {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] numbersPlusOne = IntStream.of(numbers)
            .map((value) -> increment(value, 1) )
            .toArray();
  }
}


Real World - Example 1
Code:
public class User {
  public String name;
  public String email;
  
  public User(String name, String email)
  {
    this.name = name;
    this.email = email;
  }
}

import java.util.stream.Stream;

public class main 
{   
    static void SendCorrespondence(String[] emails)
    {
      // do something
    }
  
  public static void main(String[] args) 
  {    
    User mcdonald = new User("Ronald Mcdonald", "[email protected]");
    User sanders = new User("Colonel Sanders", "[email protected]");
    User[] users = new User[]{ mcdonald, sanders };
      
    String[] emails = Stream.of(users)
               .map((user) -> user.email)
               .toArray(size -> new String[size]);
    SendCorrespondence(emails);     
  }
}


Real World - Example 2
Code:
public class User 
{
  public String name;
  public String email;
  
  public User(String name, String email)
  {
    this.name = name;
    this.email = email;
  }
}

import java.util.stream.Stream;

public class main 
{  
    static void SendSales(String[] names)
    {
      // do something
    }
  
  public static void main(String[] args) 
  {  
    User mcdonald = new User("Ronald Mcdonald", "[email protected]");
    User sanders = new User("Colonel Sanders", "[email protected]");
    User nandos = new User("Barci Nandos", "");
    User[] users = new User[]{ mcdonald, sanders, nandos };
      
    String[] names = Stream.of(users)
             .map((user) -> (user.email.length() == 0 ? user.name : null))
             .toArray(size -> new String[size]);
    SendSales(names);
  }
}


Real World - Example 3
Option type, or in the case of Java; the Optional type, work very much like you would expect a wrapper class to behave. It takes a bit getting used, but compares fairly well to Swift, but still in practice the compiler doesn't provide any assistance against Null pointer exceptions).

Also Java doesn't support defaulted (optional) parameters, which means we have overload the constructor method to simulate this. Similarly you can see I marked the primary constructor method as private and then made all the secondary constructors call it. This also helps to hide Optional<String> i.e. to simplify the API
Code:
import java.util.Optional;

public class User 
{
  public String name;
  public Optional<String> email;
  
  private User (String name, Optional<String> email)
  {   
    this.name = name;
    this.email = email;
  }
  
  public User(String name, String email)
  {
    this(name, Optional.of(email));
  }
  
  public User(String name)
  {
    this(name, Optional.empty());
  }
}

public class main 
{   
    static void SendSales(String[] names)
    {
      // do something
    }
  
  public static void main(String[] args) 
  {  
    User mcdonald = new User("Ronald Mcdonald", "[email protected]");
    User sanders = new User("Colonel Sanders", "[email protected]");
    User nandos = new User("Barci Nandos");
    User[] users = new User[]{ mcdonald, sanders, nandos };
      
    String[] names = Stream.of(users)
             .map((user) -> (!user.email.isPresent() ? user.name : null))
             .toArray(size -> new String[size]);
    SendSales(names);
  }
}


Flatmap - Example 4
Works quite elegantly, only bit to get used to is the need to use `Stream.of()` and `Stream.empty()` conversions during a transform.
Code:
import java.util.Optional;

public class User 
{
  public String name;
  public Optional<String> email;
  
  private User (String name, Optional<String> email)
  {   
    this.name = name;
    this.email = email;
  }
  
  public User(String name, String email)
  {
    this(name, Optional.of(email));
  }
  
  public User(String name)
  {
    this(name, Optional.empty());
  }
}

import java.util.stream.Stream;

public class main 
{   
    static void SendSales(String[] names)
    {
      // do something
    }
  
  public static void main(String[] args) 
  {      
    User mcdonald = new User("Ronald Mcdonald", "[email protected]");
    User sanders = new User("Colonel Sanders", "[email protected]");
    User nandos = new User("Barci Nandos");
    User[] users = new User[]{ mcdonald, sanders, nandos };
      
    String[] names = Stream.of(users)
             .flatMap((user) -> (!user.email.isPresent() ? 
                   Stream.of(user.name) : Stream.empty()))
             .toArray(size -> new String[size]);
    SendSales(names);     
  }
}


Flatten 2D Array to 1D Array
Implementation is nice; including the simple way to convert to a flatmap compatible stream `Arrays::stream`
Code:
import java.util.Arrays;
import java.util.stream.Stream;

public class main 
{
  public static void main(String[] args) 
  {   
    // 2D Jagged Array
    String[][] apartments = new String[][] {
      new String[] { "jack", "betty"},
      new String[] { "john", "nancy"},
      new String[] { "peter", "stacey"}
    };
    
    String[] occupants = Stream.of(apartments)
             .flatMap(Arrays::stream)
             .toArray(size -> new String[size]);
      
    for (String occupant : occupants) {
      System.out.println(occupant);
    }     
  }
}
...and that's it for Java, hopefully I covered it all... Next up PHP...
 
Last edited:
Nice work

Java 8 streams have been a great addition to java

I prefer how it looks with no parenthesis around single parameters in the lambda expression
 
Nice work

Java 8 streams have been a great addition to java

I prefer how it looks with no parenthesis around single parameters in the lambda expression
Thanks...

Agreed re Java... feels very concise compared to the rest of Java.
 
Article 3: PHP Code

Standard procedural process to apply a transform
Now some might ask why I even bothered to create the $mutable array, re I could have just done an in place transform of the $numbers array The reason is to be congruent with what we've done in Swift, C# and Java, process the data to simulate what occurs with value types (immutability)

A bit of background: An immutable object cannot be change after its initial creation; whilst we can try to simulate this in PHP, its not guaranteed immutable, similarly we won't be able to derive any of the benefits of value types.
PHP:
$numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
$mutable = [];
foreach ($numbers as $value) 
{
  $newNumber = $value + 1;
  array_push($mutable, $newNumber);
}
$numbersPlusOne = $mutable;

Functional equivalent
PHP:
$numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
$numbersPlusOne = array_map(function($v) { return $v + 1; }, $numbers);

Abstracting reusable code
Here we abstract away the transform to a reusable function; PHP similar to Java doesn't support type extensions like C# & Swift. Hence we'll create this as Global function, but in practive you'd probably include as a static method in an abstract class.
PHP:
function increment($value, $amount)
{
  return $value + $amount;
}

$numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
$numbersPlusOne = array_map(function($v) { return increment($v, 1); }, $numbers);

Real World - Example 1
PHP:
class User
{
  public $name;
  public $email;

  public function __construct($name, $email)
  {
    $this->name = $name;
    $this->email = $email;
  }
}

function sendCorrespondence($emails)
{
  // do something
  array_walk($emails, function($v) { print $v . ", "; });
  print "\n";
}

$mcdonald = new User("Ronald Mcdonald", "[email protected]");
$sanders = new User("Colonel Sanders", "[email protected]");
$users = [ $mcdonald, $sanders ];
$emails = array_map(function($v) { return $v->email; }, $users);
SendCorrespondence($emails);

Real World - Example 2
PHP:
class User
{
  public $name;
  public $email;

  public function __construct($name, $email)
  {
    $this->name = $name;
    $this->email = $email;
  }
}

function sendSales($names)
{
  // do something
  array_walk($names, function($v) { print $v . ", "; });
  print "\n";
}

$mcdonald = new User("Ronald Mcdonald", "[email protected]");
$sanders = new User("Colonel Sanders", "[email protected]");
$nandos = new User("Barci Nandos", "");
$users = [ $mcdonald, $sanders, $nandos ];
$names = array_map(function($v) { return strlen($v->email) == 0 ? $v->name : null; }, $users);

Real World - Example 3
PHP doesn't yet have built in support for Optional / Nullable types
A RFC has been accepted, but as yet there is no clear date for implementation
https://wiki.php.net/rfc/nullable_types

So what I decided to do in this section is include a class that tries to simulate the value boxing behavior that occurs with Nullable types, similarly to crash immediately if we try to extract the value without first testing if it has a value:
  • i.e. see ->hasValue() method.
To unwrap / unbox the value, I've added some code on the __get magic method to catch requests for the value property, and return the value if it's not null and to throw an exception if it is (i.e. to ensure a crash in build if we fail to test hasValue()).

PHP:
abstract class Category
{
  const None = -1;
  const Some = 1;
}

class Optional
{
  private $type;
  private $value;

 private function __construct($value)
  {
    if ($value == null)
    {
      $this->type = Category::None;
      $this->value = null; 
    }
    else
    {
      $this->type = Category::Some;
      $this->value = $value; 
    }
  }

  public static function none()
  {
    return new Optional(null);
  }

  public static function some($value)
  {
    return new Optional($value);
  }

  public function __get($name)
  {
    if ($name != 'value')
    {
      throw new static(print "Failed: unknown property " . $name . "\n");
    }

    if ($this->type == Category::None)
    {
      throw new static(print "Failed: tried to unwrap a Optional\Category::None");
    }
    return $this->value;
  }

  public function hasValue()
  {
    return $this->type;
  }

  public function __toString()
  {
    return $this->type == Category::None ? "Optional::None" : "Optional::Some<" . $this->value . ">";
  }
}

Here's an example of how we would use this Optional type in our code.
Basically we would first check if it has a value, before trying to access the value property
Note: This could still be a single line of code, using ternary operator
PHP:
$testOpt1 = Optional::none();
print $testOpt1->hasValue(); # -1 (false)
print $testOpt1->value; # exception thrown "Failed: tried to unwrap a Optional\Category::None"
print $testOpt1; # Optional::None

$testOpt1 = Optional::some(23);
print $testOpt1->hasValue() ; # 1 (true) 
print $testOpt1->value ; # 23
print $testOpt1; # Optional::Some<23>

$result = $testOpt1->hasValue() ? $testOpt1->value : -1; # e.g. "else" could be a default value
print $result; # 23

Real World - Example 4 - Flatmap
PHP doesn't have a flatMap method, but it's quite easy to build one, so first part will be the code for a flatMap implementation.
PHP:
/**
 ATypical flatmap implementation has the following attributes:
 - Callback transformation, 
 - Flatten 1 level (but only if it's traversable i.e. jagged array)
 - Removed any nulls
*/
function flatMap($collection, callable $callback)
{
  $flattened = [];
  foreach ($collection as $index => $element) 
  {
    $result = $callback($element, $index, $collection);
    if (is_array($result) || $result instanceof Traversable) 
    {
      foreach ($result as $item) 
      {
        $flattened[] = $item;
      }
    } 
    elseif ($result !== null) 
    {
      $flattened[] = $result;
    }
  }
  return $flattened;
}

Now for the solution using flatMap to strip the nulls
PHP:
class User
{
  public $name;
  public $email;

  public function __construct($name, $email)
  {
    $this->name = $name;
    $this->email = $email;
  }
}

function sendSales($names)
{
  // do something
  array_walk($names, function($v) { print $v . ", "; });
  print "\n";
}

$mcdonald = new User("Ronald Mcdonald", "[email protected]");
$sanders = new User("Colonel Sanders", "[email protected]");
$nandos = new User("Barci Nandos", "");
$users = [ $mcdonald, $sanders, $nandos ];
$names = flatMap($users, function($v) { return $v->email == null ? $v->name : null; });
sendSales($names);

2D Jagged Array
PHP:
$apartments = [[ "jack", "betty"],
               [ "john", "nancy"],
               [ "peter", "stacey"]];

// Flatten 2D Array to 1D Array
// Refer to the code for the flatMap function in the previous code block             
$occupants = flatMap($apartments, function($v) { return $v;} );
foreach ($occupants as $occupant)
{
  print $occupant . ", ";
}

print "\n";

Alternative way to recurse through multi-dimensional arrays
If however all you wanted to do was to recurse through all the elements irrespective of depth, then `array_walk_recursive` is a good option, except that it doesn't return the result, only a bool (success / failure), meaning it's not possible to chain actions with `array_walk_recursive`; it simply iterates through the elements and can execute a callback for each, the return type is a single bool.
PHP:
array_walk_recursive($apartments, function($v) { print $v . ", ";});

print "\n";
...and that's it for PHP, hopefully I covered it all...
Later tonight or tommorrow, I plan to post a new bonus version of the Functional Array Class for method chaining, with a few examples of how it works (versus method wrapping)
 
Last edited:
For the next article I'm thinking it may be interesting to explore combinatorial logic; i.e. combining multiple small lambda style expressions and using this result to assist with more complex transformation processes, similarly function currying usually pairs well with this topic;
The topic being Monoids.

Here's some video links covering this subject:
For the second video, Monoids are discussed towards the end of the talk, yet his entire talk is interesting; even though it's centred around F#, the underlying FP concepts are quite common across languages;

Note: I'm open to other topic suggestions, if there a particular FP area you'd like explored / explained.
 
Last edited:
PHP Bonus 2: Chained method calls for Functional Programming

As promised (quicker than I thought) -- here is a updated include (Functional.php) for chaining FP method calls.
PHP:
<?php
namespace Functional;

use ArrayObject;
use Transversable;

/**
  Extra Higher Order Functions
  */
class HOF
{
  /**
   A typical flatmap implementation
   - Callback transformation, 
   - Flatten 1 level (but only if it's traversable i.e. jagged array)
   - Removed any nulls
  */
  static function flatMap($collection, callable $callback)
  {
    InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
    $flattened = [];
    foreach ($collection as $index => $element) 
    {
      $result = $callback($element, $index, $collection);
      if (is_array($result) || $result instanceof Traversable) 
      {
        foreach ($result as $item) 
        {
          $flattened[] = $item;
        }
      } 
      elseif ($result !== null) 
      {
        $flattened[] = $result;
      }
    }
    return $flattened;
  }
}

/**
  Enumerator Sort Types 
  */
abstract class SortType
{
    const Ascending = 0;
    const Descending = 1;
    const Custom = 2;
}

/**
 Array Helper for Functional Method Chaining
 */
class FArray extends ArrayObject 
{  
  private function _arrayFunc($action, $argv)
  {
    return call_user_func_array($action, 
              array_merge(array($this->getArrayCopy()), $argv));
  }

  public static function init(...$array)
  {
    return new FArray($array);
  }

  /**
   Sort altered to return a sorted copy; to enable method chaining
   */
  private function _sort($type, $argv)
  {
    $copyOfArray = $this->getArrayCopy();
    switch ($type)
    {
      case SortType::Ascending:
        sort($copyOfArray);
        break;

      case SortType::Descending:
        rsort($copyOfArray);
        break;

      default:
        sort($copyOfArray, $argv[0]);
    }
    return new FArray($copyOfArray);
  }

  /**
    Functional Method Chaining Implementation
    
    Style Choice: 
      Method names have been shortened, 
      specifically `array_` prefix has been removed
    */
  public function __call($func, $argv)
  {
    switch ($func) 
    {
      case 'map':
        return new FArray(call_user_func_array('array_map', 
          array_merge($argv, array($this->getArrayCopy()))));

      case 'flatMap':
        return new FArray(HOF::flatMap($this->getArrayCopy(), $argv[0]));

      case 'sort':
        return $this->_sort(SortType::Ascending, $argv);

      case 'rsort':
        return $this->_sort(SortType::Descending, $argv);

      case 'reduce':
        return $this->_arrayFunc('array_reduce', $argv);

      case 'product':
        return $this->_arrayFunc('array_product', $argv);

      case 'sum':
        return $this->_arrayFunc('array_sum', $argv);

      case 'each':
      case 'walk':
        return array_walk($this, $argv[0]);

      case 'print':
        return print "[" . join(", ", $this->getArrayCopy()) . "]\n";

      case 'walkRecursive':
        return $this->_arrayFunc('array_walk_recursive', $argv);

      case 'replace':
        return $this->_arrayFunc('array_replace', $argv);

      case 'reverse':
        return new FArray($this->_arrayFunc('array_reverse', $argv));

      case 'filter':
        return new FArray($this->_arrayFunc('array_filter', $argv));

      default:
        throw new BadMethodCallException(__CLASS__.'->'.$func);
    }
  }

  public function __toString() 
  {
    return '[' . join(", ", $this->getArrayCopy()) . ']';
  }
}

/**
  Custom Asserts
  Used to validate that the passed in $collection is transversable
  */
class InvalidArgumentException extends \InvalidArgumentException
{
  public static function assertCollection($collection, $callee, $paramPosition)
  {
    self::assertCollectionAlike($collection, 'Traversable', $callee, $paramPosition);
  }

  private static function customMessage($callee, $parameterPosition, $className)
  {
    return sprintf('%s() expects parameter %d to be array or instance of %s',
      $callee,
      $paramPosition,
      $className);
  }

  private static function assertCollectionAlike($collection, $className, $callee, $paramPosition)
  {
    if (!is_array($collection) && !$collection instanceof $className) {
      throw new static(self::customMessage($callee, $paramPosition, $className));
    }
  }
}
?>

...and here's an example:
PHP:
<?php
include('Functional.php');

use Functional\FArray;

FArray::init(1, 2, 3, 23, 4, 5, 6, 7, 18, 8, 9, 36, 48)
  ->map(function($v) { return $v * 5; })
  ->flatMap(function($v) { return $v % 2 == 0 ? $v : null; })
  ->filter(function($v) { return $v % 3 == 0; })
  ->rsort()
  ->reverse()
  ->print();
      // ->walk(function($v) { return print $v . ", "; });
?>
The output from this is:[30, 90, 180, 240]
 
Last edited:
Combinatorial logic and currying would be awesome.
 
Article 4: Loading JSON with Map, Filtering & Partial Application

I decided to do something a little different for this article (it's not combinatorial logic or currying, that's later).

In order to make this article a bit less generic; I decided to tackle parsing a JSON html stream into a class instance and then executing filters for different output sets. On top of that I decided to lead with PHP this time. Anyway here's the code...

Partial Application
In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

This is particularly useful among other things for filtering; which is precisely what I'm using it for in this article. Partial Application, implies that we partially apply some of the parameters of a multi parameter function. The reason we need this, is due to the way in which the array_filter higher order function works; it iterates through all the customer and passes us a single element; a customer instance. Our filter functions however required multiple parameters, for example:
[table="width: 80%, class: outer_border"]
[tr]
[td]To filter out the customers within a group of countries, we need the following bits of information (parameters):
  • List of countries
  • The name of the property that stores the country name e.g. 'country'
  • Lastly the class instance in order to evaluate if we have a match
As you can see that's 3 parameters, but the array_filter (higher order function) only can work with 1 parameter functions i.e. pass in the customer class instance, and receive back a bool value indicating if we have a match. So we first need to simplify our filter from 3 parameters to 1.
[/td]
[/tr]
[/table]
To do this we take advantage of a feature in PHP that allows us to pass in an array of values as a substitute for the parameters, secondly we construct another function with 1 missing value and return that result to be stored in a variable i.e. a variable that contains the partially applied known parameter values: (our matching property and list of countries).

For the next part I'm going to let code speak for itself. You'll need 3 php files namely:
  • Functional.php (this is code for the partial application & method chaining; it's an updated version)
  • ascii_tables.php (This is a github project that eases the output of the results to ASCII formatted tables)
  • TestJSON.php -- the code for this article (read the comments)

Code for TestJSON.php
PHP:
<?php
require_once('Functional.php');
require_once('ascii_tables.php');
# Reference: https://github.com/pgooch/PHP-Ascii-Tables

use Functional\FArray;
use Functional\HOF;

#--------------------------------------------------------------------------------------
# Customer Class (JSON is converted into instances of this, see loadJSON method)
#--------------------------------------------------------------------------------------
class Customer
{
  public $name;
  public $city;
  public $country;
  public $timestamp;

  public function __construct($name, $city, $country)
  {
    $this->name = $name;
    $this->city = $city;
    $this->country = $country;
    $this->timestamp = date(DATE_ATOM);
  }

  public function __toString()
  {
    return $this->name . " " . $this->city . " " . $this->country;
  }

  public static function loadJSON($json)
  {
    return new Customer($json->Name, $json->City, $json->Country);
  }
}

#--------------------------------------------------------------------------------------
# Generic function used for filtering
# These two functions (hasPrefix & isInArray) are abstracted reusable filters
#--------------------------------------------------------------------------------------
function startsWith($haystack, $needle) {
  // search backwards starting from haystack length characters from the end
  return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
}

/**
  Generic Filter by starting prefix 
  */
function hasPrefix($property, $prefix, $class)
{
  $reflectionClass = new ReflectionClass($class);
  $propertyValue = $reflectionClass->getProperty($property);
  return startsWith(strtoupper($propertyValue->getValue($class)), strtoupper($prefix));
}

/**
  Generic filter by property, array and class
  */
function isInArray($property, $array, $class)
{
  $reflectionClass = new ReflectionClass($class);
  $propertyValue = $reflectionClass->getProperty($property);
  return in_array($propertyValue->getValue($class), $array);
}

#--------------------------------------------------------------------------------------
# JSON process starts here
#--------------------------------------------------------------------------------------
date_default_timezone_set('Africa/Johannesburg');

$json = @file_get_contents('http://www.w3schools.com/website/customers_mysql.php');
if ($json == FALSE) 
{ 
  exit("Unable to load JSON : Customers");
}

# Decode JSON
$decodedJSON = json_decode($json);

# Create ASCII table instance (demo output)
$ascii_table = new ascii_table();

# Partial Load of Customer static method called loadJSON
# Simplifies for array_map and ->map
$loadCustomer = HOF::partialApplication('Customer::loadJSON');

# --------------------------------------------------------------------------------------
# Standard use of PHP Higher Order Functions
# Filtering by Countries
# --------------------------------------------------------------------------------------
// Load our Customers
$customers = array_map($loadCustomer, $decodedJSON);

# Partial Application of filters; basically we preload all the known parameters.
# The only remaining parameter is the class instance; which gets passed in by the array_filter
# Note: The partial application function assigns a partially loaded function pointer to a variable
#       A variable that only needs one more value to execute (the customer instance)
$selectedCountries = HOF::partialApplication('isInArray', 'country', ['Germany', 'Denmark']);
$selectedCities = HOF::partialApplication('isInArray', 'city', ['London', 'Berlin', 'Vancouver']);
$selectedNames = HOF::partialApplication('hasPrefix', 'name', 'Gal');

# Standard filter with array_filter
$filter_countries = array_filter($customers, $selectedCountries);

# Chained Methods Map & Filter Examples;
# Note: for performance we should only map the customer once, 
#       repetition here is solely for demonstration purposes
$customers2 = FArray::init($decodedJSON)->map($loadCustomer);
$filter_cities = $customers2->filter($selectedCities);
$filter_names = $customers2->filter($selectedNames);

# Output the results
print $ascii_table->make_table($filter_countries, 'Filtered Countries [Germany, Denmark]') . "\n";
print $ascii_table->make_table($filter_cities, 'Filtered Cities [London, Berlin, Vancouver]') . "\n";
print $ascii_table->make_table($filter_names, 'Filtered Name Prefix: Gal') . "\n";
?>

Code for ascii_table (https://github.com/pgooch/PHP-Ascii-Tables)
Please download this directly from github i.e. to ensure you have the latest updates.

Note: The updated Functional.php will follow in a separate post (too long)

Here's what the output should look like:
Code:
                 Filtered Countries [Germany, Denmark]
+------------------+-------------+---------+---------------------------+
| name             | city        | country | timestamp                 |
+------------------+-------------+---------+---------------------------+
| Alfreds Futterkiste | Berlin      | Germany | 2016-09-15T06:14:14+02:00 |
| Königlich Essen  | Brandenburg | Germany | 2016-09-15T06:14:14+02:00 |
| Simons bistro    | København   | Denmark | 2016-09-15T06:14:14+02:00 |
| Vaffeljernet     | Århus       | Denmark | 2016-09-15T06:14:14+02:00 |
+------------------+-------------+---------+---------------------------+

                     Filtered Cities [London, Berlin, Vancouver]
+-------------------------------+-------------+---------+---------------------------+
| name                          | city        | country | timestamp                 |
+-------------------------------+-------------+---------+---------------------------+
| Alfreds Futterkiste           | Berlin      | Germany | 2016-09-15T06:14:14+02:00 |
| Laughing Bacchus Wine Cellars | Vancouver   | Canada  | 2016-09-15T06:14:14+02:00 |
| North/South                   | London      | UK      | 2016-09-15T06:14:14+02:00 |
+-------------------------------+-------------+---------+---------------------------+

                              Filtered Name Prefix: Gal
+-------------------------------+-------------+---------+---------------------------+
| name                          | city        | country | timestamp                 |
+-------------------------------+-------------+---------+---------------------------+
| Galería del gastrónomo        | Barcelona   | Spain   | 2016-09-15T06:14:14+02:00 |
+-------------------------------+-------------+---------+---------------------------+
PS... Table output isn't perfect... sorry not my code...
 
Last edited:
Article 4: PHP Part 2 (Functional.php)

Code for Functional.php (updated version, includes Partial Application)
PHP:
<?php
namespace Functional;

use ArrayObject;
use Transversable;

/**
  Extra Higher Order Functions
  */
class HOF
{
  /**
   A typical flatmap implementation
   - Callback transformation, 
   - Flatten 1 level (but only if it's traversable i.e. jagged array)
   - Removed any nulls
  */
  static function flatMap($collection, callable $callback)
  {
    InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
    $flattened = [];
    foreach ($collection as $index => $element) 
    {
      $result = $callback($element, $index, $collection);
      if (is_array($result) || $result instanceof Traversable) 
      {
        foreach ($result as $item) 
        {
          $flattened[] = $item;
        }
      } 
      elseif ($result !== null) 
      {
        $flattened[] = $result;
      }
    }
    return $flattened;
  }

  /**
   Partial Application
   PHP can substitute an array for parameters
   http://www.php.net/manual/en/function.call-user-func-array.php
  */
  static public function partialApplication(/* $function, $arguments... */)
  {
    // Retrieve passed in arguments
    $arguments = func_get_args();

    // Retrieve function (1st element)
    $function = array_shift($arguments);

    // Return a new function
    return function() use ($function, $arguments)
    {
      return call_user_func_array($function, array_merge($arguments, func_get_args()));
    };
  }
}

/**
  Enumerator Sort Types 
  */
abstract class SortType
{
    const Ascending = 0;
    const Descending = 1;
    const Custom = 2;
}

/**
 Array Helper for Functional Method Chaining
 */
class FArray extends ArrayObject 
{  
  private function _arrayFunc($action, $argv)
  {
    return call_user_func_array($action, 
              array_merge(array($this->getArrayCopy()), $argv));
  }

  public static function init($array)
  {
    return new FArray($array);
  }

  public static function initSequence(...$array)
  {
    return new FArray($array);
  }

  /**
   Sort altered to return a sorted copy; to enable method chaining
   */
  private function _sort($type, $argv)
  {
    $copyOfArray = $this->getArrayCopy();
    switch ($type)
    {
      case SortType::Ascending:
        sort($copyOfArray);
        break;

      case SortType::Descending:
        rsort($copyOfArray);
        break;

      default:
        sort($copyOfArray, $argv[0]);
    }
    return new FArray($copyOfArray);
  }

  /**
    Functional Method Chaining Implementation
    
    Style Choice: 
      Method names have been shortened, 
      specifically `array_` prefix has been removed
    */
  public function __call($func, $argv)
  {
    switch ($func) 
    {
      case 'map':
        return new FArray(call_user_func_array('array_map', 
          array_merge($argv, array($this->getArrayCopy()))));

      case 'flatMap':
        return new FArray(HOF::flatMap($this->getArrayCopy(), $argv[0]));

      case 'sort':
        return $this->_sort(SortType::Ascending, $argv);

      case 'rsort':
        return $this->_sort(SortType::Descending, $argv);

      case 'reduce':
        return $this->_arrayFunc('array_reduce', $argv);

      case 'product':
        return $this->_arrayFunc('array_product', $argv);

      case 'sum':
        return $this->_arrayFunc('array_sum', $argv);

      case 'each':
      case 'walk':
        return array_walk($this, $argv[0]);

      case 'print':
        if ($argv[0] == true)
        {
          return print join("\n", $this->getArrayCopy()) . "\n";
        }
        return print "[" . join(", ", $this->getArrayCopy()) . "]\n";

      case 'walkRecursive':
        return array_walk_recursive($this, $argv[0]);
        // return $this->_arrayFunc('array_walk_recursive', $argv);

      case 'replace':
        return $this->_arrayFunc('array_replace', $argv);

      case 'reverse':
        return new FArray($this->_arrayFunc('array_reverse', $argv));

      case 'filter':
        return new FArray($this->_arrayFunc('array_filter', $argv));

      case 'keys':
        return new FArray($this->_arrayFunc('array_keys', $argv));

      default:
        throw new BadMethodCallException(__CLASS__.'->'.$func);
    }
  }

  public function __toString() 
  {
    return '[' . join(", ", $this->getArrayCopy()) . ']';
  }
}

/**
  Custom Asserts
  Used to validate that the passed in $collection is transversable
  */
class InvalidArgumentException extends \InvalidArgumentException
{
  public static function assertCollection($collection, $callee, $paramPosition)
  {
    self::assertCollectionAlike($collection, 'Traversable', $callee, $paramPosition);
  }

  private static function customMessage($callee, $parameterPosition, $className)
  {
    return sprintf('%s() expects parameter %d to be array or instance of %s',
      $callee,
      $paramPosition,
      $className);
  }

  private static function assertCollectionAlike($collection, $className, $callee, $paramPosition)
  {
    if (!is_array($collection) && !$collection instanceof $className) {
      throw new static(self::customMessage($callee, $paramPosition, $className));
    }
  }
}
?>
 
Last edited:
Article 4: PHP Bonus (Currency JSON example)

The topic of processing currency rates JSON API has come up before, so I thought there might be some interest to add a FP example:
PHP:
<?php
require_once('Functional.php');
require_once('ascii_tables.php');
# Reference: https://github.com/pgooch/PHP-Ascii-Tables

use Functional\FArray;
use Functional\HOF;

#--------------------------------------------------------------------------------------
# Customer Class (JSON is converted into instances of this, see loadJSON method)
#--------------------------------------------------------------------------------------
class Currency
{
  public $name;
  public $rate;
  public $timestamp;

  public function __construct($name, $rate)
  {
    $this->name = $name;
    $this->rate = $rate;
    $this->timestamp = date(DATE_ATOM);
  }

  public function __toString()
  {
    return $this->name . " " . $this->rate . " " . $this->timestamp;
  }

  public static function loadJSON($array, $key)
  {
    return new Currency($key, $array[$key]);
  }
}

#--------------------------------------------------------------------------------------
# Generic function used for filtering
#--------------------------------------------------------------------------------------
function isInArray($array, $value)
{
  return in_array($value, $array);
}

#--------------------------------------------------------------------------------------
# JSON process starts here
#--------------------------------------------------------------------------------------
date_default_timezone_set('Africa/Johannesburg');

$json = @file_get_contents('http://api.fixer.io/latest');
if ($json == FALSE) 
{ 
  exit("Unable to load JSON : Customers");
}

# decode, filter & load
$selectedCurrencies = ['ZAR', 'GBP', 'USD', 'CAD'];
$decodedJSON = json_decode($json, true);
$base = $decodedJSON['base'];
$currencyFilter = HOF::partialApplication('isInArray', $selectedCurrencies);
$loadCurrency = HOF::partialApplication('Currency::loadJSON', $decodedJSON['rates']);
$filtered_currencies = FArray::init($decodedJSON['rates'])
                            ->keys()                   # Extract the currency codes 
                            ->filter($currencyFilter)  # Filter out the codes we need
                            ->map($loadCurrency);      # Load the rates for the remainder

# print
$ascii_table = new ascii_table();
print $ascii_table->make_table($filtered_currencies, "http://api.fixer.io/latest vs. " . $base) . "\n";
?>

Here's an example of the output:
Code:
      http://api.fixer.io/latest vs. EUR
+------+---------+---------------------------+
| name | rate    | timestamp                 |
+------+---------+---------------------------+
| CAD  | 1.4799  | 2016-09-15T08:04:16+02:00 |
| GBP  | 0.85078 | 2016-09-15T08:04:16+02:00 |
| USD  | 1.1218  | 2016-09-15T08:04:16+02:00 |
| ZAR  | 16.0776 | 2016-09-15T08:04:16+02:00 |
+------+---------+---------------------------+
Hope it helps...
 
Last edited:
Top
Sign up to the MyBroadband newsletter
X