Shallow: Cloning a Hook-up App in F#

July 2014

If you’re looking to pass superficial judgement on hot nerds in F# t-shirts,
you’ve come to the right place.

F# is a functional programming language designed with an emphasis on data science, that can also be used to build native iOS and Android apps with Xamarin. In my experience, when people discuss F#, someone inevitably asks “F# is great for analysis, but is it good for creating user interfaces?” I created a simple Tinder clone for iOS to show that F# is actually awesome for creating UIs, and for building apps in general.

Another ‘sharp’ language?

F# is heavily inspired by OCaml, a functional language known for its pragmatic blend of functional features with a familiar object model. This inherited design decision is still bearing fruit, as F# makes it possible to build iOS and Android apps using object-oriented APIs like UIKit, while incorporating incredibly useful functional features like algebraic datatypes, pattern matching, immutability, and more. I’ll show you how I used a mix of functional and OO features to build a slick, interactive UI.

The gateway drug of functional languages

For the uninitiated, functional programmers can seem like demented wizards–they utter arcane-sounding words like functor, and get inordinately excited over tiny functions that appear to do basically nothing. Thankfully, F# can resemble a leaner version of C# when you want it to, so it’s easy to start using it. For example, here I’ve defined a type encapsulating an animation that shoots a view outside of another view:

type ShootOutAnimator(referenceView: UIView) =
    let speed = 1300.0f
    let animator = UIDynamicAnimator(referenceView)
    member this.ShootOut(view: UIView, direction: Direction) =
        let x, y = direction.UnitVector
        let shoot = UIDynamicItemBehavior(view, AngularResistance=2.0f)
        shoot.AddLinearVelocityForItem(PointF(x * speed, y * speed), view)
        shoot.AddAngularVelocityForItem(x * 3.0f, view)
        shoot.Action <- fun () ->
            if not (referenceView.Bounds.IntersectsWith(view.Frame)) then

Pretty vanilla, right? Any Python, Java, or C# developer would understand at least 90% of what’s happening here.

As you become more comfortable with F#, you’ll start using functional features in your more traditionally OO code. For example, here I make an algebraic datatype for specifying directions to the ShootOutAnimator, and I define a read-only UnitVector property using type inference, pattern matching and tuples:

type Direction =
    Up | Down | Left | Right

    member this.UnitVector =
        match this with
        | Up -> (0.0f, 1.0f)
        | Down -> (0.0f, -1.0f)
        | Left -> (-1.0f, 0.0f)
        | Right -> (1.0f, 0.0f)

This lets me get the x and y components for a given screen direction like:

let (x, y) = Left.UnitVector

Finally, you can write code in a predominantly functional style. Here I create an asynchronous computation that downloads photos from an infinite stream of photo URLs, uses a higher-order memoize function to cache the results, and hides state within a closure (technically a deferred computation built with an async workflow, which is F#’s euphemism for “monad”):

let nextPhoto =
    let urls = (Seq.cycle photos).GetEnumerator()
    let getImage = memoize UIImage.FromUrl
    async {
        return getImage urls.Current

If you’re new to FP, you’re probably feeling a bit queasy after that last code snippet. That’s okay–the nextPhoto computation could have been implemented as a 25-line C#-style method, but I love that F# gives me such a range of expression.

These snippets come together to make this delightful interaction (full source):

I hope I’ve convinced you that compelling UIs in F# are at least possible, and that it’s a great language for gradually learning the functional concepts that are having an increasing impact on mainstream programming. If you’re still skeptical, download Xamarin, clone this app, and see for yourself! Send me an interesting pull request and I’ll mail you an F# t-shirt :smile: