Sunday, August 9, 2015

Approaching the Fold

"Approach the fold and cull th' infected forth"
-- Shakespeare, Timon of Athens
Act V, Scene IV, Line 43

Welcome


In Graham Hutton's excellent paper, "A tutorial on the universality and expressiveness of fold", the first function that is looked at is And.  Dr. Hutton gives And the following definition:

and :: [Bool] → Bool
and = fold (∧) True

The idea being that we give Fold an And function with a seed value of True.  If one of the members of the collection given is False the Anding it with True will yield a value of False and thus the end result will be False.  If no member of the collection given is False then the end result will be True.


Let us look at this in code.

Clojure


(ns fold-and)
(defn fold-and
"and = fold (∧) True"
[& facts]
(reduce #(and %1 %2) true facts))
view raw Fold_And.clj hosted with ❤ by GitHub
(ns fold-and.tests
(require
[clojure.test :refer [deftest testing are is run-tests]]
[fold-and :as sut]))
(deftest fold-and-tests
(testing "Given all true it must return true"
(are [truths] (true? (apply sut/fold-and truths))
[true]
[true true]
(repeat 100 true)))
(testing "Given all false it must return false"
(are [falsehoods] (false? (apply sut/fold-and falsehoods))
[false]
[false false]
(repeat 100 false)))
(testing "Given some true and some false it must match every's result"
(are [facts] (= (every? identity facts) (apply sut/fold-and facts))
[true]
[false true]
[true true]
[false true]
[false false]
(repeat 100 true)
(repeat 100 false))))
(run-tests)


To create an Anding function we'll use reduce along with the and macro with a seed of true.  Since and is a macro, we'll need to wrap it in a function in order to be able to use it in reduce.

C#


using System.Linq;
namespace Folders
{
public class FoldAnd
{
public bool And(params bool[] facts)
{
return facts.Aggregate(true, (m, x) => m && x);
}
}
}
view raw FoldAnd.cs hosted with ❤ by GitHub
using System.Linq;
using Folders;
using Xunit;
namespace FoldersTests
{
public class FoldAndTests
{
[Theory]
[InlineData(new[] { true })]
[InlineData(new[] { true, true })]
[InlineData(new[] { true, true, true })]
public void GivenTrueValuesItMustReturnTrue(bool[] truths)
{
var sut = new FoldAnd();
var actual = sut.And(truths);
Assert.True(actual);
}
[Theory]
[InlineData(new[] { false })]
[InlineData(new[] { false, false })]
[InlineData(new[] { false, false, false })]
public void GivenFalseValuesItMustReturnFalse(bool[] falsehoods)
{
var sut = new FoldAnd();
var actual = sut.And(falsehoods);
Assert.False(actual);
}
[Theory]
[InlineData(new[] { true })]
[InlineData(new[] { false, true })]
[InlineData(new[] { true, true })]
[InlineData(new[] { false, false })]
[InlineData(new[] { false, false, false })]
[InlineData(new[] { false, true, false })]
[InlineData(new[] { true, true, true })]
public void GivenSomeTrueAndSomeFalseValuesItMustReturnFalse(bool[] facts)
{
var expected = facts.All(a => a);
var sut = new FoldAnd();
var actual = sut.And(facts);
Assert.Equal(expected, actual);
}
}
}
view raw FoldAndTests.cs hosted with ❤ by GitHub


We'll use the LINQ function Aggregate along && and a value of true to create an Anding function.

JavaScript


var _ = require("lodash");
exports.foldAnd = function foldAnd(/*facts*/) {
var facts = _.toArray(arguments);
return _.foldl(facts, function(m, x) {return m && x}, true);
};
view raw foldAnd.js hosted with ❤ by GitHub
var sut = require("../src/foldAnd"),
expect = require("expect.js"),
_ = require("lodash");
describe("foldAnd", function(){
it("Given all true it must return true", function(){
expect(sut.foldAnd(true)).to.be.ok();
expect(sut.foldAnd(true, true)).to.be.ok();
expect(sut.foldAnd(true, true, true)).to.be.ok();
}),
it("Given all false it must return false", function(){
expect(sut.foldAnd(false)).not.to.be.ok();
expect(sut.foldAnd(false, false)).not.to.be.ok();
expect(sut.foldAnd(false, false, false)).not.to.be.ok();
}),
it("Given some true and false it must return the same result as every", function(){
expect(sut.foldAnd(true, false)).to.equal(_.every([true, false]));
expect(sut.foldAnd(false, true)).to.equal(_.every([false, true]));
expect(sut.foldAnd(false, true, false)).to.equal(_.every([false, true, false]));
expect(sut.foldAnd(false, false, true)).to.equal(_.every([false, false, true]));
})
});
view raw foldAndSpec.js hosted with ❤ by GitHub


We see that by using lodash's foldl function with && and the value true we can create an Anding function.

ECMAScript 2015


let _ = require("lodash");
exports.foldAnd = (...facts) =>
_.foldl(facts, (m, x) => m && x, true);
view raw foldAnd.js hosted with ❤ by GitHub
let sut = require("../src/foldAnd"),
expect = require("expect.js"),
_ = require("lodash");
describe("foldAnd", () => {
it("Given all true it must return true", () => {
expect(sut.foldAnd(true)).to.be.ok();
expect(sut.foldAnd(true, true)).to.be.ok();
expect(sut.foldAnd(true, true, true)).to.be.ok();
}),
it("Given all false it must return false", () => {
expect(sut.foldAnd(false)).not.to.be.ok();
expect(sut.foldAnd(false, false)).not.to.be.ok();
expect(sut.foldAnd(false, false, false)).not.to.be.ok();
}),
it("Given some true and false it must return the same result as every", () => {
expect(sut.foldAnd(true, false)).to.equal(_.every([true, false]));
expect(sut.foldAnd(false, true)).to.equal(_.every([false, true]));
expect(sut.foldAnd(false, true, false)).to.equal(_.every([false, true, false]));
expect(sut.foldAnd(false, false, true)).to.equal(_.every([false, false, true]));
})
});
view raw foldAndSpec.js hosted with ❤ by GitHub


Since we live in the future, we'll use Babel to allow us to use ECMAScript 2015.  We see again that by using lodash's foldl with && along with the value true we now have an Anding function.

Fin


There you have it the And function using Fold, showing once again that all you need is Fold.