Programming Theory and Practice

Note: This is a bit of a personal reflection exploratory post; if you are looking for deep insights into the Swift language or programming in general, you're probably not going to find much here.

A little Swift aside for some context?

So why did I even start to go down this route of exploration into FRP? Well, I wanted to create an API that felt natural in Swift for interacting with the IOHIDManager class for a prototype of a game idea that I have (thanks to much motivation from Handmade Hero). This is the only way that know how to interact with multiple gamepads, keyboards, and mice to build a proper platform layer for a game on OS X (and potentially iOS).

It turns out, building this in Swift is a pretty bad experience. The IOHIDManager class relies on C callback functions for device connection, removal, and input. This is the point in my Swift work that I know is just going to be painful as I've done it multiple times now. The only way for this to work 1 is to:

  1. Create a C file (and corresponding header) to actually implement the callbacks
  2. Export that callback in a variable so that Swift can use it
  3. Create another Swift class with the @objc attribute so that I can pass data from C back into Swift via this class. Of course, this class needs to be public which artificially adds an implementation detail to the public interface, which I also really dislike.

Ok, this is easy to do, but annoying. Also, at this point, I have now introduced a lot of overhead for dealing with input events, both in terms of code required and runtime costs. I don't like this at all… and I need to do for keyboard, gamepad, and mouse events.

What's really annoying, I just simply wanted to start prototyping with something like this:

public enum VirtualKey {
    case A
    // ...
}

public enum KeyState {
    case Up
    case Down
}

public struct MergedKeyState {
    let key: VirtualKey
    let start: KeyState
    let transitions: Int
}

public struct KeyboardInput {
    var samples: [VirtualKey : [KeyState]]

    public init() {
        self.samples = [VirtualKey : [KeyState]]()
        self.samples[VirtualKey.A] = [KeyState]()
    }

    public mutating func add(key: VirtualKey, state: KeyState) {
        samples[key]?.append(state)
    }

    public mutating func key(key: VirtualKey) -> [KeyState] {
        if let input = self.samples[key] {
            self.samples[key]?.removeAll()
            return input
        }
        else {
            return []
        }
    }

    public mutating func keyA() -> MergedKeyState? {
        let merged: MergedKeyState? = reduce(self.key(.A), nil) {
            merged, state in
            if let merged = merged {
                return MergedKeyState(key: merged.key,
                    start: merged.start,
                    transitions: merged.transitions + 1)
            }
            else {
                return MergedKeyState(key: .A, start: state, transitions: 0)
            }
        }

        return merged
    }
}

Is this a good approach to the problem? I don't know, that's why I'm prototyping out potential solutions. There is a bunch of mutating attributes, that might be bad… but the point is, the only way I could actually start playing with this is to remove it from the context of the problem all together because of the C interop issues.

So what's my point?

Excellent question! Honestly, I don't even know if I'm quite sure. I have a lot of thoughts in my head around many tangentially related topics, but I guess if we focus it merely on this trend in functional programming and FRP implementations, I feel like I have to be missing something because all I really see is this: a very primitive architecture for a game engine.

If you are using "pull" to get the data, you really are simply handling input just like many game engines would. That input cascades changes down some structure of data to determine what needs to be updated and re-drawn.

If you are using a "push" model, you've really just implemented a potentially nicer way doing exactly what you are doing today with event driven or notification driven UI patterns. Yes, there are some nice things we can do here, but this seems to continue with one of the biggest problems: mutations from random places at random times, albeit, maybe slightly more controlled.

I guess at the end of it all I have a bit of melancholy about the whole situation. I wish more people were thinking up these models and really applying them to the hardware instead of using the hardware to, essentially, brute force the conceptual model into working on our current hardware.

  1. As of Xcode 6.3 beta 2…
Programming Theory and Practice