Posted Posted in Life

I recently rediscovered Lego, thanks to my nephews. Hands down the greatest of all “toys”. I wonder if my parents still have the bin full I had as kid? I’ve started a new collection in any event…

Posted Posted in Life

C. S. Lewis, foreshadowing:

Of all tyrannies, a tyranny sincerely exercised for the good of its victims may be the most oppressive. It would be better to live under robber barons than under omnipotent moral busybodies. The robber baron’s cruelty may sometimes sleep, his cupidity may at some point be satiated; but those who torment us for our own good will torment us without end for they do so with the approval of their own conscience. They may be more likely to go to Heaven yet at the same time likelier to make a Hell of earth. This very kindness stings with intolerable insult. To be “cured” against one’s will and cured of states which we may not regard as disease is to be put on a level of those who have not yet reached the age of reason or those who never will; to be classed with infants, imbeciles, and domestic animals.

A difference in perspective

Posted Posted in Development

Recently, Erica Sadun wrote about inconsistencies in Swift’s function-like constructs, such as KeyPath and method references—a post that appears lost in the apocalypse befalling her site.

Erica pointed out that method references return (A) -> () -> B functions that aren’t directly useable by higher order functions like map, which takes an (A) -> B function.

// Basic example
["a", "b"].map { "a".uppercased() } // ["A", "B"]

// Using a method reference. Hmm. Not quite what we wanted
["a", "b"].map(String.uppercased) // [() -> String, () -> String] 

// Junky alternatives
["a", "b"].map(String.uppercased).map { $0() }
["a", "b"].map { String.uppercased($0)() }

She then works through a couple of solutions. A custom map style function on Sequence taking such a function and hiding the extra function application, and eventually an apply function that converts (A) -> () -> B to (A) -> B (and an accompanying operator).

One thing that struct me was how Erica derived all this from first principles, essentially reimplementing a function called flip, a common function in functional programming circles. From my perspective I immediately saw the problem as:

Oh, I need to flip this function and partially apply it to ()

Unfortunately, because we’re using Swift, it’s not quite that easy.

func flip<A, B, C>(f: (A) -> (B) -> C) -> (B) -> (A) -> C {
    return { b in { a in f(a)(b) } }
}

// Doesn't compile! For a couple of Swiftastic reasons.
["a", "b"].map(flip(String.uppercased)())

Swift treats () parameters as a special case, not just another type, so () can not be used as a B. Let’s write a special case version:

func flip<A, B>(_ f: @escaping (A) -> () -> B) -> () -> (A) -> B {
    return { { a in f(a)() } }
}

// This one does compile! Though you may notice it looks to have some redundancy.
["a", "b"].map(flip(String.uppercased)())

And we can now reimplement Erica’s apply in terms of our special case of flip:

func flap<A, B>(_ f: @escaping (A) -> () -> B) -> (A) -> B {
    return flip(f)()
}

["a", "b"].map(flap(String.uppercased))

Or just remove the intermediary special case of flip altogether, arriving at exactly Erica’s solution (operator not withstanding):

func flap<A, B>(_ f: @escaping (A) -> () -> B) -> (A) -> B {
    return { a in f(a)() }
}

["a", "b"].map(flap(String.uppercased))

In the end, we’ve arrived at the same solution to this specific problem. However, I hope I’ve illustrated how viewing programming from a slightly different perspective let us identify the issue quickly, and build a solution out of existing smaller, composeable pieces.