• Swift TIL 8

    Although I read up on it a few days back, it was only this evening that I fired up Emacs (well of course I’m testing Swift with Emacs, what did you think I’d do?!?) and dabbled with some code to really get a feel for how casting works in Swift.

    Swift seems to be one of those languages that does it with a keyword rather than a non-keyword approach. The two main keywords being as? and as!. I kind of like how there’s a polite version and a bossy version. Having the two makes a lot of sense from what I read too.

    To try and illustrate my understanding so far (and do keep in mind I’m not writing this with the express purpose of explaining it to anyone else – I’m writing this to try and retain it all in my head by working through it), here’s a bit of utterly pointless and highly-contrived code that defines three classes in a fairly ordinary hierarchy:

    class Animal {}
    
    class Dog : Animal {}
    
    class Cat : Animal {
        func beAdorable() {
            print( "Purrrrrr!" )
        }
    }
    

    So, so far, so good: we have animals, we have dogs which are a kind of animal, and we have cats, which are also a kind of animal, but they have the special ability of actually being adorable. 😼

    Now, for the purposes of just testing all of this out, here’s a horrible function that makes little sense other than for testing:

    func adore( _ a : Animal ) {
        ( a as! Cat ).beAdorable()
    }
    

    Given an animal, it forces it to be a cat (by casting it with as!), and then asks it to be adorable (because, of course, cats always do as they’re asked).

    So, if we then had:

    adore( Cat() )
    

    we’d get what we expect when run:

    Purrrrrr!
    

    So far so good. But what about:

    adore( Dog() )
    

    Yeah, that’s not so good:

    ~/d/p/s/casting$ swift run
    [3/3] Linking casting
    Could not cast value of type 'casting.Dog' (0x10a1f8830) to 'casting.Cat' (0x10a1f88c0).
    fish: 'swift run' terminated by signal SIGABRT (Abort)
    

    One way around this would be to use as?, which has the effect of casting the result to an Optional. This means I can re-write the adore function like this:

    func adore( _ a : Animal ) {
        ( a as? Cat )?.beAdorable()
    }
    

    Now, if a can be cast to a Cat, you get an optional that wraps the Cat, otherwise you get an optional that wraps nil (hence the second ? before attempting to call the beAdorable member function).

    So now, if I run the problematic dog call above again:

    ~/d/p/s/casting$ swift run
    [3/3] Linking casting
    

    In other words, no output at all. Which is the idea here.

    I think I like this, I think it makes sense, and I think I can see why both as! and as? exist. The latter also means, of course, that you can do something like:

    func adore( _ a : Animal ) {
        let cat = a as? Cat
        if cat != nil {
            cat!.beAdorable()
        } else {
            print( "That totally wasn't a cat" )
        }
    }
    

    which, in the messy dog call again, now results in:

    ~/d/p/s/casting$ swift run
    [3/3] Linking casting
    That totally wasn't a cat
    

    Or, of course, the same effect could be had with:

    func adore( _ a : Animal ) {
        if a is Cat {
            ( a as! Cat ).beAdorable()
        } else {
            print( "That totally wasn't a cat" )
        }
    }
    

    It should be stressed, of course, that the example code is terrible design, so given the above I’d ensure I never end up in this sort of situation in the first place. But for the purposes of writing and compiling and running code and seeing what the different situations result in, it helped.

  • Swift TIL 7

    This post is very much a case of me writing it down to try and get it all straight in my head, and to make sure it sticks. The other day I was reading about Swift’s types and type-equality checks, and as I’d expect from plenty of other languages I’ve worked with, there’s a way for checking that two types are the same, such that super/subclasses aren’t taken into account, and a way where they are. So, given this silly code:

    class Animal {}
    
    class Cat : Animal {}
    
    print( Cat.self == Animal.self )          // False
    print( Cat.self is Animal.Type )          // True
    print( type( of: Cat() ) is Animal.Type ) // True
    

    it’s made clear that == checks for strict equality and a super/subclass relationship isn’t taken into account. On the other hand is does take it into account.

    Only… what’s with this whole .self sometimes and .Type other times business? That took a little bit of writing code and playing to get comfortable with. Here’s how I understand it now (and do feel free to correct me below if I’m way off):

    Given the above code, Animal.Type is the type of a value that expresses the type of Animal. On the other hand, Animal.self is a value that is the type of an Animal. Yeah, I know, that still reads oddly. But written as code:

    let feline : Cat.Type = Cat.self
    

    I think it makes a lot more sense. And having got there I felt I better understood it. I’m not 100% sure I’m 100% with it, but I’m getting there.

  • Swift TIL 6

    I’m going to file this one under “it seems really unnecessary, but it’s also kinda cool”. While reading up about protocols the book I’m reading introduced the ExpressibleBy*Literal protocols, where the * is one of a number of obvious literals. For example: ExpressibleByStringLiteral. As you might imagine, it lets you create a class that can be initialised with a literal value, as opposed to needing to appear to call the constructor for a class.

    So, for a silly example:

    class Hello : ExpressibleByStringLiteral {
    
        private let what : String
    
        required init( stringLiteral what : String ) {
            self.what = what
        }
    
        func say() {
            print( "Hello, \(self.what)!" )
        }
    }
    

    You could, of course, write this:

    let v1 = Hello( "world" )
    v1.say()
    

    but because of ExpressibleByStringLiteral you can also write:

    let v2 : Hello = "universe"
    v2.say()
    

    Now, sure, in this case it saves you nothing, but this does also mean that parameters of functions whose type uses one of the ``ExpressibleBy*Literal` protocols can be passed a literal, rather than a “long-hand” instantiated object. For example:

    func Greet( _ h : Hello ) {
        h.say()
    }
    
    Greet( "davep" )
    

    I can see that being quite handy.

  • Swift TIL 5

    I’m going to file this one under “it makes perfect sense, but I don’t think it aids in making the code readable” – something which I’m finding is a bit of a theme with Swift.

    In Swift self gets used and gets used in a way you’d expect from many other languages. So far so good. But, it seems, Self is also a thing too, and it’s different from self. Whereas self is the current instance, Self is the current type. So consider this:

    class Greeting {
    
        class func message() -> String {
            "Ayup!"
        }
    
        func message() -> String {
            "Well hello there"
        }
    
        func emit() {
            print( Self.message() )
            print( self.message() )
        }
    }
    
    Greeting().emit()
    

    When run, the output is:

    Ayup!
    Well hello there
    

    It makes sense that that’s the case. I actually really like the ability to use Self rather than having to use Greeting, but damn that’s really setting things up for either mistyping or misreading code. You’re going to want to hope that your development environment makes it super obvious what’s a value and what’s a type when it comes to using font choices.

  • Swift TIL 4

    Some languages favour a “one way to do it” approach, some favour “there’s more than one way to do it”. I’m not sure I’m at a point where I have a feel for what Swift’s approach is, but I’m getting the impression it’s more the latter than the former.

    If there was one thing that made me think that, it was when I found out that Swift’s bool type has a toggle method.

    var cool = false
    
    print( cool )
    cool = !cool
    print( cool )
    cool.toggle()
    print( cool )
    

    giving:

    $ swift run
    false
    true
    false
    

    I can see a number of reasons why that’s actually going to be handy – the main one being when you want to throw around a toggling method – but it still struck me as rather odd on first reading. I also think it’s worth me making a personal note about it because foo.toggle() isn’t going to stand out as much as foo = !foo when reading code. At least not for a short while.

subscribe via RSS