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, Kevin 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.

Naming Errors #swiftlang

In recent betas, Apple extended ErrorType for structs and classes, as well as enumerations. Even so, enumerations still win for quick-and-simple error reporting:

enum MyErrorEnumeration: ErrorType {case FirstError, SecondError, ...}

So what do you name your errors?

When dealing with trivial applications, feel free to use trivial names.

enum Error: ErrorType {case WrongFile, ItsMonday, IFeelCranky}

But when you expand errors to more significant use-cases, what’s a sound set of rules for naming? I don’t really have any good answers yet, but here are a few thoughts. Please pick these apart and suggest alternatives:

  • Use the word Error in enumeration names. Yes: FileProcessingError, No: FileProcessing
  • Describe the error circumstances in the enumeration case. Yes:FileNotFound, No:Missing.
  • Differentiate cases with associated values, case FileNotFound(fileName: String)
  • Don’t force enumerations when it’s easier to use classes or structs. If you need line tracking, source files, and free-form reason text, consider using a more appropriate type.

How to Print: the Beta 6 edition #swiftlang

Forget everything new about printing. RIP appendNewline. RIP last-position output stream. Print is new, redesigned, and totally facelifted in Beta 6.

Here’s what those public faces look like (and check out those hot non-terminal variadics):

public func print(items: Any..., separator: String = default, terminator: String = default) 

public func print<Target : OutputStreamType>(items: Any..., separator: String = default, terminator: String = default, inout toStream output: Target)

In its most basic form, you still print things:

print(something)

and Swift will use a variety of protocols (Streamable, CustomStringConvertible, CustomDebugStringConvertible, those do not appear to have changed) to write a textual representation to stdout.

In Swift 2.0 beta 6 and above, you can print several things at once:

print(thing, anotherThing, yetAnotherThing)

Two special parameters (separator and terminator) control what happens between items and at the end of lines. An output parameter toStream controls where text is sent.

So if you’re writing an IRC client, you might send both \r and \n.

print(myText, terminator:"\r\n", toStream:&myIRCStream)

Or you might create a comma delimited list of integers:

print(1, 5, 2, 3, 5, 6, separator:", ")

You skip newlines by overriding the default terminator:

print("on one line", terminator:""); print("on the same line")

As with earlier Swift 2.0’s, strings conform to OutputStreamType, so you can print to strings:

var string =""; print("hello", toStream:&string)

Probably the most useful thing about print is that its new variadic arguments are all Any, so you can create a heterogeneous list of strings and items of interest, e.g.

print("My number is ", myNumber, " And this one is ", otherNumber)

without having to use in-line escaping (although it remains there if you really want it). This way, if your function calls use quote marks you don’t have to go through the compute then print work-arounds you had to in earlier betas.

Here’s a quick overview of what this all looks like:

Screen Shot 2015-08-24 at 5.53.29 PM

Up the mix with Beta 6 #swiftlang

New in Beta 6

I talked about try? in a separate post because it really deserves a longer discussion than quick bullet points.

Completion. Context-sensitive Xcode completion for inferred dot syntax, e.g. MyEnum.Item vs .Item, when the type is clear to the compiler. neat.

Screen Shot 2015-08-24 at 5.41.17 PM

Static Computed Properties in Protocol Extensions. Yay. You can, I believe, also use required protocol members in computing those properties.

Screen Shot 2015-08-24 at 5.42.53 PM

Non-Terminal Variadics. They can appear anywhere in the parameter list, enabling you to most importantly combine variadics with trailing closures.

Screen Shot 2015-08-24 at 5.47.07 PM

Collection Clarity. Non-ObjC types cannot be stored within ObjC-marked properties, e.g. an Array of swift-specific enumerations. Collections containing types that are not Objective-C compatible are no longer considered Objective-C compatible types themselves. For example, previously Array<SwiftClassType> was permitted as the type of a property marked @objc. This is no longer the case. (19787270)

Block imports are now typealiases for Swift-style closures. I’ll just quote Apple: C typedefs of block types are now imported as typealiases for Swift closures. The primary result of this is that typedefs for blocks with a parameter of type BOOL are now imported as closures with a parameter of type Bool (rather than ObjCBool as in the previous beta). This matches the behavior of block parameters to imported Objective-C methods. (22013912)

Type Aliases now get an aka annotation in error messages. So you can see what they alias to for better clarity.

Screen Shot 2015-08-24 at 5.48.35 PM

Print and Debug Print on Steroids. They’re now variadic and you can add a String separator. (I wish they had called it separator and not string because better Update: they did.).  Quick separate post on this. It’s really cool stuff.

public func print(items: Any..., separator: String = default, terminator: String = default)
public func print<Target : OutputStreamType>(items: Any..., separator: String = default, terminator: String = default, inout toStream output: Target)

Screen Shot 2015-08-24 at 5.53.29 PM
RIP appendNewline.

RIP extend(). Long live appendContentsOf(). Same bat behavior, new bat name. Time to go back and fix all my code. *shakes fist*.

All collections are SliceableGinsu!

Most closure ops are rethrows-able. So you can now throw out of maps and filter, and you can try? and &&/||/?? together.

Screen Shot 2015-08-24 at 5.58.44 PM

Screen Shot 2015-08-24 at 5.58.52 PM