A paint pot analogy of functional programming in Swift


Everyone who has ever used even the most basic of art programs on a computer has seen a perfect example of functional principles (even if functional programming wasn't used to write the code behind the program). Take for example the paint pot (or fill tool). You first of all select the tool and then choose a colour. Or perhaps you do the same thing the other way around. It doesn't matter, a colour is passed to the paint pot either way. But nothing happens yet, it is primed and ready to go but it needs more information: what to fill. And that doesn't happen until later and it might also happen a number of times before the tool or the colour is changed.

Taking shape

A similar thing happens with shape tools. Let's take for example a rectangle tool. At first the only thing that the tool knows is what colour it will fill itself with. It knows nothing of the size or location of the rectangle it will draw until the user inputs that information, but again it might draw several rectangles of the same colour of different sizes in different locations.

Typically this would be done by storing the colour information and the tool information in a property that could be updated and changed. This seems to make sense because colours change and so it should be mutable. But let's look at it from a functional perspective and write a function that will return a function, so that when the rectangle tool is clicked the first function is called (rather than a variable property changed) and this then provides the function for drawing the shape once the location and size is known.
typealias PositionShape = CGRect -> CAShapeLayer

func rectangle(color:UIColor) -> PositionShape {
    let shape = CAShapeLayer()
    shape.fillColor = color.CGColor
    return {
        rect in
        let path = CGPathCreateMutable()
        CGPathAddRect(path, nil, rect)
        shape.path = path
        return shape
    }
}
As in previous posts on functional programming, I've adopted the approach from Functional Programming in Swift, where a typealias defines the function that is returned from the first function. We can now call this in two steps, mirroring what a user would do in an illustration app, first clicking on the rectangle tool at which point a check of the selected colour can be made and that colour can be passed to the rectangle function (similarly if the rectangle tool is selected and the colour changed then the same function would be called).
let rectPainter = rectangle(UIColor.blueColor())
Having primed the rectangle, and having a function stored as a constant labelled rectPainter (in this instance), we can now make as many calls to this function as we like and each time a CAShapeLayer containing a blue rectangle will be returned.
let rect = rectPainter(CGRect(x: 50, y: 50, width: 100, height: 100))
And for completeness we can now add the CAShapeLayer to a view:
// add returned subview to a view
let view = UIView(frame: CGRect(x: 0,y: 0,width: 200,height: 200))
view.layer.addSublayer(rect)
view // display view in Playground

Real-world use

In real-world use a device is only going to inform us of the position at which touches began and touches ended. If we want a CGRect to pass to our PositionShape function then we'll need to construct that from the two touch co-ordintates. But of course we'll know where touches began before we know where they ended. This is something, once again, that happens in two parts and can benefit from a function that returns a function:
typealias ShapeRectangle = CGPoint -> CGRect

func startingPoint(point:CGPoint) -> ShapeRectangle {
    return {
        endPoint in
        let startPointX = point.x < endPoint.x ? point.x : endPoint.x
        let startPointY = point.y < endPoint.y ? point.y : endPoint.y
        return CGRect(x: startPointX, y: startPointY, width: abs(endPoint.x-point.x), height: abs(endPoint.y-point.y))
    }
}

let object = startingPoint(CGPoint(x: 150, y: 150))
let objectRect = object(CGPoint(x: 50, y: 50))
Using a function that returns a function we end up with an implementation that feels very natural: a start point is passed to a function when the touches begin and an endpoint is sent to the result of that function when the touches end.

A gist with the full code is available to cut and paste into a playground.


Endorse on Coderwall

Comments