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.

Selecting better frames

How should I frame this? Selecting better video frames for photogrammetry

Posted Posted in Development, Diving

Sometimes my spheres of interest overlap.

Meeting Matt Carter at OzTek a couple of years ago, and working with Pim Bongaerts on mesophotic.org introduced me to photogrammetry, and over the past few weeks I’ve been processing some models using 4K video.

Selecting useable still frames is tedious at best: manual selection is a non starter, and basic selection with ffmpeg often produces blurred frames.

I solved this with framer.sh, a script for selecting better frames.

Feel free to use and improve it. Pull requests accepted.