Type Inference in Swift

I have a confession to make. I’m really tired of writing iflets and guards all over the place in my code.

 

There are some really basic problems that I see:

  • You are struggling with Swift instead of expressing, what needs to be done;
  • You don’t have a central access point to the casting code;
  • You are not robust to Swift API changes;
  • You have to write more code and autocomplete won’t save you$
  • You don’t use type inference.

First of all, let me introduce the casting function I use:

Yep, that’s just a wrapper above as?. Why is it good? It allows casting to types without considering the preprocessing limitations of Swift, that were created for the mere reason of helping out the devs, that are clueless about the types. A perfect example is an Int?? type (yep, optional wrapped in optional, you didn’t misread it) that we want to cast to Int?. And guess what, compiler won’t let you do that with as?:

Compiler fails with Downcast from 'Int??' to 'Int?' only unwraps optionals; did you mean to use '!'?. This means, that the compiler can do that, but, it thinks it knows better, what the programmer wanted, so it tries to help by stressing, that by unwrapping Int?? into Int? we receive Optional as the result. Oh thank you so much, Captain Obvious, we couldn’t have guessed it without your valuable help.

 

So, another solution is to use generic types. You see, from the point of view of Swift itself (if we could have disabled sanity checks for programmers), the type in generic could be any type, including function, optional, array or anything else. And, you guessed it, Int?? as well as Int? are also separate types. Moreover, as? itself could be used for unwrapping (although, the reasons behind why casting could result in unwrapping are behind my understanding and both AST and SIL don’t provide any clues about such a behavior).

 

So, if we take a look at the cast function above if we wanted to cast and unwrap from Int?? type to Int?, we could just write:

This would print us: Optional(1). If, on the other hand, we try to cast Int?? to Int type system would behave as expected and wouldn’t print anything:

However, such a function, although being type inference – enabled and chainable isn’t particularly useful in terms of the amount of code you have to write. This could be alleviated by using functors, applicative or monads. I can confess, that I’m making my first steps into functional programming, so I’ll cover those types in a later blog posts, after I manage to get a hold of the ideas behind FP and how to mix them with OOP changeable state with the help of a friendly functional community (kudos to all lispers and haskellers, who answer my stupid questions and write free open-source books explaining difficult concepts in a simple manner, you are the best, guys). For now, let’s just define those types in simple incorrect manner:

  • Functor – type, who implements map;
  • Applicative – type, who implements apply;
  • Monads – type, who implements flatMap.

So, let’s take a real-life problem instead. Suppose we have an Int property and we want to put Any in it if its value is castable to Int:

We can’t directly assign the Any value into Int property, so we should use either iflet or guard for unwrapping:

The amount of code for the person with objc background, who is used to nil being a valid value, is horrific. Moreover, don’t forget, that you can’t send messages to values wrapped into Optional directly, unlike objc, where it’s a valid operation (unless you send the messages, where the result is a struct). How would our cast function help in that case?

That’s much better and as long, as you remember, that cast returns an Optional, this code is much more expressive. Overall, even if you don’t, the code is still expressive, as long, as you at least remember what a map is (apply a function to a wrapped value, if the value is legitimate). All the types, in that case, are inferred by the compiler without any external guidance, so we can write a simpler more light-weight typesafe code with less verbose syntax.

 

Still, just imagine writing such casts for each cell type, as in our previous example. The duplication and struggling with the verbose inexpressive syntax is what awaits us. And that’s far from DRY we all try to abide (we do try that, don’t we?). Moreover, there are is another obvious flaw: cast is unchainable, unless you bloat your code.

The result of the above cast + map + assignment is ()? (or Void?, if you are more used to it). This means, that the second map in a row wouldn’t do us any good, as we can’t actually do anything useful with unwrapped ().

 

But that’s the tale for another day. We’ll dive into making the casting chainable in one of my next articles.

cast function is available at cocoapods IDPCastable and is available on github as well: IDPCastable.

 

That’s all, folks. Have a great day and stay DRY, no matter, where you are.

Comments

Your photo