Creating Medusa Tuples in Swift

One day, when I was home doing terrible things to my dog with a fork, it hit me… – Steve Martin

When I was a student at Oberlin college, one of my CS professors, Rich Salter (not that one), had come up with a cute hack to represent repeating decimal numbers in Scheme so that they lost no precision. He called them Medusa numbers because everything was well and good until you looked at them, then you’d freeze because the print out was infinite.

medusa_by_carvaggio

Keep this in mind.

Swift is an interesting language. It is neither functional nor procedural, but a hybrid of the two. It draws the line closer to the procedural side so as to be friendlier to Objective-C programmers, whereas F# cleaves much closer to the functional side.

Before I get into my specific problem, let’s examine the Swift landscape. Swift is a language wherein it is required that everything is initialized. This is important to Swift because Swift is a reference counted language. Unfortunately, like many other memory management schemes, you have to be aware of what is happening or you’re likely to cause problems. C is like this too, but the problems are much more manifest. Swift hides them, but the problems are still there.

Let’s say that you do this in Swift:

var x:SomeStruct = SomeStruct()
x = aFunctionThatReturnsSomeStruct();

What you likely think is happening is less than what actually happens. First you might say, “x gets set to the return value of the SomeStruct() constructor.” And you’re nearly right. X, however, does not get set with that value. Instead, x gets initialized with the return value of the constructor. “Then,” you continue, “x gets set with the return value of aFunctionThatReturnsSomeStruct”. Not quite. First, Swift calls the function, holding onto the return, then it looks up a special function in the value witness table for SomeStruct and calls a destructor method in it to destroy the contents of x. Then Swift calls a function to copy the return value from the function onto x.
This distinction is very important and it all has to do with Swift’s automatic reference counting (ARC).

One day a student came to Moon and said: “I understand how to make a better garbage collector. We must keep a reference count of the pointers to each cons.”
Moon patiently told the student the following story:
“One day a student came to Moon and said: `I understand how to make a better garbage collector…

I do not care for reference counting as a memory management scheme.

So let’s consider this little gem that you can do in C#:

T x = default(T); // T is a generic type parameter

You can’t do this in Swift. When I say that, I’m lying. You can, but the code I’ve seen that uses it scares the pants off of me. Here is just such code:

public func defaultOf<T>() -> T {
    let x = UnsafeMutablePointer<T>.alloc(a)
    let retval = x.memory
    x.dealloc()
    return retval
}

And this code works. Almost. The problem arises if your T is a struct or enum that contains a reference type (either directly or indirectly or in a sub element), you have created a time bomb.

let x:SomeStruct = defaultOf() // mmmaybe ok?
x = aFunctionThatReturnsSomeStruct() // ...and you're dead

The reason is that when the destructor gets called before assignment, any reference types in get their reference count decremented, but the value is unknown, probably null and then you get a bus error. If you’re lucky.
Ultimately, default(T) can’t or shouldn’t exist in Swift because Swift insists on knowing that variable in Swift is initialized and meaningful and default(T) can’t guarantee that.

The only escape clause is in the types UnsafePointer and UnsafeMutablePointer. And this is where I finally get to my problem: I needed to interoperate with Swift from C, specifically, I had to call a function that took a Swift optional. An optional in Swift is syntactic sugar implemented in a generic enum (more or less):

public enum Option { case none, some(T) }

The problem is that in C I can neither generate nor consume Swift enums. The spec in the Swift ABI is very scant for how enums are laid out. There are 5 different cases. Some are straightforward, others not so much. What I can do is crack apart a Swift tuple because the rules for the memory layout of tuples is much better specified. So I started with two functions in Swift:

public func toOptional<T>(optTuple: (value: T, isPresent: Bool)) -> Bool
{
   if isPresent { return value; }
   else { return nil }
}
public func fromOptional<T>(value: T?, result: UnsafeMutablePointer<(T, Bool)>)
{
   if value != nil {
      result = (value, true);
   }
   else {
       let x = UnsafeMutablePointer<Bool>(result + 1)
       x.initialize(false)
   }
}

And here you see that I’ve done a terrible thing to Swift with a fork. I’ve created a tuple that, if a nil optional comes in, will have an invalid head which is not consumable in Swift. And this is a Medusa Tuple. If you don’t take precautions before looking at the head, you’re just dead.
But what about toOtional? Does it work? Yes, because it never touches the value unless it’s valid. So even if I can’t call a function in Swift that takes or returns an optional in C, I can write a wrapper around the Swift function that transforms optionals to/from tuples and then calls the desired function.

I hope that if you find this you only find it in desperation and that the last thing you do is use the defaultOf function above because it is fundamentally broken in Swift semantics. Instead, take the “Unsafe” label very seriously. Values within an unsafe pointer may not be initialized and this is one of the few places in Swift where this is allowable. Swift also cares very much about the lifetime of memory. In my own code, the memory for the UnsafeMutablePointer is allocated in C and deallocated in C and that how it should be.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.