Archive for August, 2015

Tropical Storm Error-ca #swiftlang

When you extend String to support ErrorType, you can just throw strings as errors. Does it get any simpler than this?

extension String: ErrorType {}
func raiseError(reason : String) throws {
    throw reason}
do {try raiseError("immediate failpoint")} 
    catch {print(error)}

Actually, I’m kind of fond of this too:

extension String: ErrorType {}
extension ErrorType {
    public func contextString(
        file : String = __FILE__, 
        function : String = __FUNCTION__, 
        line : Int = __LINE__) -> String {
        return "\(function):\(file):\(line) \(self)"
    }
}
func raiseError() throws {
    throw "failpoint".contextString()}
do {try raiseError()} catch {print(error)}

Naming Methods and Functions

Feel free to critique. Also see: this and this.

Be both brief and clear. Add nouns to contextualize verbs and prepositions. Prefer removeObject(object, atIndex: index) over remove(object, at: index). Clarity should never suffer from an excess of brevity.

Avoid abbreviations. Prefer printError(myError) to printErr(myErr) and setBackgroundImage(myImage) to setBGImage(myImg). Although Apple offers a list of “acceptable” abbreviations, avoid them in Swift outside of gimmes like max and min.

Avoid ambiguity. Consider whether a function or method name has multiple interpretations. For example, in displayName, is the word display a noun or a verb? If it’s unclear, re-work the name to remove that confusion.

Be consistent. Use the same terms throughout your apps and libraries to describe concepts. Avoid using fetchBezierElements() in one method and listPathComponents() in another.

Don’t reference constructs. Avoid struct, enum, class, instance, and object in your names. Prefer buildDeckOfCards() to buildDeckOfCardsStruct().

Use lower-case for method names. Although most developers use lower-case for functions outside a type scope, you can capitalize without committing a moral crime. Uppercase function names immediately differentiate themselves from methods even though they are currently out of fashion. I’ve capitulated for the most part but this may be a battle worth fighting again. This practice was quite common up to and including name-spacing when it suddenly went extinct. It’s like a million capitalized voices cried out and were suddenly silenced

Skip “get”. Functions that retrieve state information should describe the thing they’re returning. Prefer extendedExecutionIsEnabled() or isExtendedExecutionEnabled() to getExtendedExecutionIsEnabled(). Limit get to cases where data is returned through a parameter.

Describe arguments with labels. Encourage reading past the function name to incorporate labels into how the function describes itself. The results create descriptors that incorporate prepositions like with, of, and between. You “construct color with red, green, and blue”, test the “length of string”, or “test equality between x and y”.

A natural fluency relates a function name and its labels to a story about how the function will be used. The result is self-documenting, without relying on memory or look-up to determine which parameters and types are to be provided in each ordered spot. Prefer withTag: to tag:.

Use prepositions, avoid “and”. And is the one word that Apple specifically calls out to avoid. Instead of initializing with view and position, use “view, position” arguments.

If you feel you must use and, reserve it to when there’s a semantic link between a group of arguments, such as constructing colors with “red, green, and blue” floating point values. In such cases, it’s unlikely that future keyword tweaks will interrupt the relationship between these items. Even in such cases, purists will disapprove and treat your use as code smell.

The one case where Apple endorses and use is when a method describes two distinct actions, e.g. openFile(withApplication:, andDeactivate:).

Use the word value after a type-based label. Prefer toIntValue to toInt, or withCGRectValue to withCGRect.

Use American phrases where standard. Prefer initialize to initialise and color to colour as these words are Apple-supplied.

When in doubt, mimic Apple. Search for an Apple API with a similar concept and use that method signature as a guideline. Be prepare to be inspired by Objective-C. As a rule, not all Apple APIs have been reviewed for Swift. Their automatic translation may not offer sufficiently well-considered examples.

Beta 6: flatten #swiftlang

The new flatten function is an odd creature. You’d think it would work like flatMap and it kind of sort of does but not entirely.

Here’s a quick example:

let array = [[1], [2, 3, 4], [5], [6, 7]]
print(array.flatMap({$0})) 
    // [1, 2, 3, 4, 5, 6, 7]
print(array.flatten()) 
    // FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1], [2, 3, 4], [5], [6, 7]])
print(Array(array.flatten())) 
    // [1, 2, 3, 4, 5, 6, 7]

The flatten function returns a new seemingly lazy collection. I’m guessing it’s fairly similar to:

print(array.lazy.flatMap({$0}))
    // Swift.LazyMapCollection<Swift.Array<Swift.Array<Swift.Int>>, Swift.Array<Swift.Int>>(_base: [[1], [2, 3, 4], [5], [6, 7]], _transform: (Function))))

And like most collection / sequences, you can iterate over the results or force them to an array, etc.

There are actually 6 different versions of flatten in beta 6, which seem to start with collections of collections and build from there. The fourth of these is a real doozy.

  • extension CollectionType where Generator.Element : CollectionType
  • extension CollectionType where Generator.Element : CollectionType, Index : BidirectionalIndexType, Generator.Element.Index : BidirectionalIndexType 
  • extension LazyCollectionType where Generator.Element : CollectionType, Elements.Generator.Element : CollectionType, Generator.Element == Elements.Generator.Element
  • extension LazyCollectionType where Generator.Element : CollectionType, Index : BidirectionalIndexType, Generator.Element.Index : BidirectionalIndexType, Elements.Generator.Element : CollectionType, Elements.Index : BidirectionalIndexType, Elements.Generator.Element.Index : BidirectionalIndexType, Generator.Element == Elements.Generator.Element
  • extension LazySequenceType where Elements.Generator.Element == Generator.Element, Generator.Element : SequenceType
  • extension SequenceType where Generator.Element : SequenceType

More complex items can be called with flatten() but don’t necessarily return the thing you might expect:

let complexerArray = [[[1]], [[2]], [[3]], [[4, 5]]]
print(Array(complexerArray.flatten())) 
    // [[1], [2], [3], [4, 5]]

You can chain .flatten().flatten() to get [1, 2, 3, 4, 5] in this example but you can’t apply the function to an already flat collection.

I’m sure there’s a deeply compelling reason to prefer flatten over flatMap for simple flattening (it’s certainly easier to incorporate with a function call vs a closure) but I’m a little baffled as to why it’s popped up in the standard library, especially with such bizarre and tortured declarations.

The big win of today’s exercise seems to be standardizing on some greppage:

grep func $0 | sed "s/^ *//" | sed "s/.*func //" | grep -v "///" | sort | uniq | open -f

which you can then diff -y, and search for < and >. Today’s keywords appear to be advance, distance, extend, splice on the Beta 5 side (advancedBy and distanceTo are the renamed versions) and appendContentsOf, flatten, and insertContentsOf on Beta 6.

 

Capturing context #swiftlang

Say you’re working with a type and you want to throw an error that reflects the context of where it originates. You can do that, mostly, using a few built-in compiler keywords. __FUNCTION__, __LINE__, and __FILE__ provide literal interpolation about the details of a calling function:

public struct Error: ErrorType {
    let source: String; let reason: String
    public init(_ reason: String, source: String = __FUNCTION__,
        file: String = __FILE__, line: Int = __LINE__) {
            self.reason = reason; self.source = "\(source):\(file):\(line)"
    }
}

A typical line of output for Error looks like this example:

Error(source: "myFunction():<EXPR>:14", reason: "An important reason")

While this struct enables you to capture the error’s function, file, and line, you can’t capture the originating parent type without a type parameter. To fetch that type, extend the Error initializer to include a “source type”, and pass self.dynamicType from the constructor.

public struct Error: ErrorType {
    let source: String; let reason: String
    public init(_ reason: String, type: Any = "", 
        source: String = __FUNCTION__,
        file: String = __FILE__, 
        line: Int = __LINE__) {
            self.reason = reason; self.source = "\(source):\(file):\(line):\(type)"
    }
}

I find the extra type parameter deeply annoying. The entire point of this approach is to simplify error generation.

public struct Parent {
    func myFunction() throws {
        throw Error("An important reason", type: self.dynamicType)}
}

do {try Parent().myFunction()} catch{print(error)}
// Error(source: "myFunction():<EXPR>:14:Parent", reason: "An important reason")

Use a protocol to automatically pick up on type context. The default implementation in the following Contextualizable extension refers to self.dynamicType in a way that a method signature cannot.

protocol Contextualizable {}
extension Contextualizable {
    func currentContext(file : String = __FILE__, function : String = __FUNCTION__, line : Int = __LINE__) -> String {
        return "\(file):\(function):\(line):\(self.dynamicType)"
    }
}

Combining approaches enables you to simplify the whole shebang. The shared Error type reduces to simple lets and context responsibility moves from the Error initializer to a conforming type, automatically inheriting the currentContext method.

public struct Error: ErrorType {
    let source: String; let reason: String
    public init(_ source: String = __FILE__, _ reason: String) {
        self.reason = reason; self.source = source
    }
}
public struct Parent: Contextualizable {
    func myFunction() throws {
        throw Error(currentContext(), "An important reason")}

The updated results now includes the originating type in the string output.

As reader Kametrixom points out,  you can also extend Contextualizable to construct an error on your behalf. (He’s also written a really nice error type that optionally adds context.)

Here’s a link to a gist where I’ve thrown up some of this all together.

Beta 6 Lazy #swiftlang

Until Beta 6, lazy was defined as a standalone function. (I discussed it in this old post.)

public func lazy<Base : SequenceType>(s: Base) -> LazySequence<Base>

Beta 6 introduces a lazy property on sequences.

extension SequenceType {
    public var lazy: LazySequence<Self> { get }
}

This property is implemented in CollectionType, LazyCollectionType, and LazySequenceType extensions. You use it to delay evaluation on things like map and filter that are normally eager.

So, starting with Beta 6, instead of calling the function like this:

let words = "lorem ipsum elit"
    .characters.split(isSeparator: {$0 == " "}) 
let counts = lazy(words).map(count)

You use the in-line property like this:

let counts = words.lazy.map({$0.characters.count})

Thanks, Lily Ballard

Rec request: secure storage on common 3rd party cloud solutions

Anyone able to recommend a secure storage solution for common vendors like Dropbox, iCloud, or Box? I’m looking for something that is:

  • compatible with existing accounts
  • accessible from OS X and iOS, primarily for loading files from OS X and reading from iOS
  • provides in-cloud encryption either file-by-file or sparse encrypted disks (but must offer file-by-file from the client)
  • Offers encrypt/decrypt speed and read times that are reasonably responsive. They don’t have to be super-fast but it shouldn’t be an order of magnitude slower than using the standard Dropbox client

Alternatively, if you know of a good secure use-your-home-computer client that’s iOS and OS X friendly, I’d love to hear about that too.

Thanks in advance

Beta 6 Playgrounds #swiftlang

Playgrounds introduced some significant changes with Beta 6. Most importantly, the timeline slider appears to have disappeared.

Update: Norio Nomura found it:

 

Screen Shot 2015-08-26 at 4.16.10 PM

You can click on intermediate calculation points in an embedded value history to view those values as tiny pop-ups.

Screen Shot 2015-08-26 at 11.31.18 AM

The Quick Look system is redesigned for both the value history and the results gutter. Instead of the old-style [=,~,o] buttons in the embedded value history, you now either see just a single value, a graph or a list:

Screen Shot 2015-08-26 at 11.30.57 AM

Update: Kelly G writes in the comments, “Also if you Control-Click on the inline results of a loop, you can toggle between Latest Value, Value History, Graph.”

Screen Shot 2015-08-26 at 5.32.54 PM

The only hover-to-reveal action that remains is for list Quick Looks:

Screen Shot 2015-08-26 at 11.31.07 AM

You can no longer close value summaries in the main timeline. Click the filled circles in the gutter instead.

The Beta 6 version of the Playgrounds Book is updated and available now in iTunes. I’m pushing another update with a few more errata today that should hit the store tomorrow or the next day, and I’ll continue updating as I find new features.

How to Join and Extend in Beta 6 #swiftlang

I talked about the Beta 6 differences to print earlier this week but the Swift standard library introduced other significant changes with this update. Here are a few to consider.

Join has become joinWithSeparator, which you call after a sequence, interposing a separator between the elements of a sequence. For example:

["a", "b", "c"].joinWithSeparator(" ") // "a b c"
print(Array([[1, 2], [1, 2], [1, 2]]
    .joinWithSeparator([0]))) 
    // [1, 2, 0, 1, 2, 0, 1, 2]

The sequence must be composed of strings or items that are themselves of sequence type, so you can use this with arrays but not with, for example, ints.

If you used extend to add contents to arrays and other range-replaceable collections (and, oh yes, I did), update that code to use appendContentsOf().

var x = [1, 2, 3]
x.appendContentsOf([4, 5, 6])
x // [1, 2, 3, 4, 5, 6]

The renaming also affected splice, which has the new related name insertContentsOf(). Supply a sequence to insert and an index to add it at.

x.insertContentsOf([0, 0], at: 2)
x // [1, 2, 0, 0, 3, 4, 5, 6]

The stride function, a commonly used fave for me, now operates on a Strideable type, so instead of from-through/to-by, you:

print(Array(1.0.stride(through: 3.0, by: 0.5)))
    //[1.0, 1.5, 2.0, 2.5, 3.0]

None of these introduce completely new features the way print did, but they’re certainly worth noting especially since the Swift migrator seems to mess up the stride migration, where it took a lot of by-hand-tweaking to get things working right.