What’s new in swift 4.2


Good news: Swift 4.2 is now available in Xcode 10 beta! This release updates important Swift 4.1 features and improves the language in preparation for ABI stability.

1. toggle() for Bool

swift propose adding a mutating func toggle to Bool. It toggles the Bool.

var isVisible = true
print(isVisible.toggle())  // false

2. Adding in-place removeAll(where:) to the Standard Library

It is common to want to remove all occurrences of a certain element from a collection. This proposal is to add a removeAll algorithm to the standard library, which will remove all entries in a collection in-place matching a given predicate.

in Swift 3

var nums = [1,2,3,4,5]
// remove odd elements
nums = nums.filter { !isOdd($0) }

In Swift 4.2

nums.removeAll(where: isOdd)

3. count(where:)

While Swift’s Sequence models brings a lot of niceties that we didn’t have access to in Objective-C, like map and filter, there are other useful operations on sequences that the standard library doesn’t support yet. One current missing operation is count(where:), which counts the number of elements in a Sequence that pass some test.

Swift-evolution thread: count(where:) on Sequence

In Swift 3

[1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3

In swift 4.2

[1, 2, 3, -1, -2].count(where: { $0 > 0 }) // => 3

4. Introduce compactMapValues to Dictionary,

Swift 4 introduced two new Dictionary operations: the new method mapValues and a new version of filter. They correspond to the Sequencemethods map and filter, respectively, but they operate on Dictionaryvalues and return dictionaries rather than arrays.

However, in Swift 4 left a gap in the API: it did not introduce a Dictionary-specific version of compactMap. We sometimes need to transform and filter values of a Dictionary at the same time, and Dictionary does not currently provide an operation that directly supports this.

For example, consider the task of filtering out nil values from a Dictionaryof optionals:

let d: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let r1 = d.filter { $0.value != nil }.mapValues { $0! }
let r2 = d.reduce(into: [String: String]()) { (result, item) in result[item.key] = item.value }
// r1 == r2 == ["a": "1", "c": "3"]

Or try running a failable conversion on dictionary values:

let d: [String: String] = ["a": "1", "b": "2", "c": "three"]
let r1 = d.mapValues(Int.init).filter { $0.value != nil }.mapValues { $0! }
let r2 = d.reduce(into: [String: Int]()) { (result, item) in result[item.key] = Int(item.value) }
// r == ["a": 1, "b": 2]

In Swift 4.2

let d: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let r4 = d.compactMapValues({$0})
// r4 == ["a": "1", "c": "3"]

Or,

let d: [String: String] = ["a": "1", "b": "2", "c": "three"]
let r5 = d.compactMapValues(Int.init)
// r5 == ["a": 1, "b": 2]

5. introducing a new compiler directive that is syntactically equivalent to the swift directive but checks against the version of the compiler,

  • stop bumping old Swift versions when new versions are introduced.

This will simplify future Swift versions by stopping the artificial growth of old language version.

This change is possible because it retains the ability to conditionally compile code targeting a compiler in compatibility mode:

#if swift(>=4.1) && compiler(>=5.0)
// Code targeting the Swift 5.0 compiler and above in --swift-version 4 mode and above.
#endif

It will also greatly simplify conditional compilation based on compiler version alone:

#if swift(>=4.1) || (swift(>=3.3) && !swift(>=4.0))
// Code targeting the Swift 4.1 compiler and above.
// This can't change because it needs to continue working with older compilers.
#endif
#if compiler(>=4.2)
// Code targeting the Swift 4.2 compiler and above.
#endif
#if compiler(>=5.0)
// Code targeting the Swift 5.0 compiler and above.
#endif

6. Add an allSatisfy algorithm to Sequence

You can achieve this in Swift 3 with contains by negating both the criteria and the result:

// every element is 9
!nums.contains { $0 != 9 }
// every element is odd
!nums.contains { !isOdd($0) }

but these are a readability nightmare. Additionally, developers may not make the leap to realize contains can be used this way, so may hand-roll their own for loop, which could be buggy, or compose other inefficient alternatives:

// misses opportunity to bail early
nums.reduce(true) { $0.0 && $0.1 == 9 }
// the most straw-man travesty I could think of...
Set(nums).count == 1 && Set(nums).first == 9

in Swift 4.2 , Introduce an algorithm on Sequence which tests every element and returns true if they all match a given predicate:

nums.allSatisfy(isOdd)

on the basis that it aids readability and avoids performance pitfalls from the composed alternatives.

7. Random API

For the core API, introduce a new protocol named RandomNumberGenerator. This type is used to define RNGs that can be used within the stdlib. Developers can conform to this type and use their own custom RNG throughout their whole application.

Then for the stdlib’s default RNG implementation, introduce a new struct named SystemRandomNumberGenerator.

Next, we will make extension methods for FixedWidthIntegerBinaryFloatingPoint and Bool. For numeric types, this allows developers to select a value within a range and swap out the RNG used to select a value within the range.

FixedWidthInteger example:

// Utilizes the standard library's default random
// Alias to:
// var rng = SystemRandomNumberGenerator()
// Int.random(in: 0 ..< 10, using: &rng)
let randomIntFrom0To10 = Int.random(in: 0 ..< 10)
let randomUIntFrom10Through100 = UInt.random(in: 10 ... 100, using: &myCustomRandomNumberGenerator)
// The following are examples on how to get full width integers
let randomInt = Int.random(in: .min ... .max)
let randomUInt = UInt.random(in: .min ... .max, using: &myCustomRandomNumberGenerator)

BinaryFloatingPoint example:

// Utilizes the standard library's default random
// Alias to:
// var rng = SystemRandomNumberGenerator()
// Float.random(in: 0 ..< 1, using: &rng)
let randomFloat = Float.random(in: 0 ..< 1)
let randomDouble = Double.random(in: 0 ... .pi, using: &myCustomRandomNumberGenerator)

Bool example:

// Utilizes the standard library's default random
// Alias to:
// var rng = SystemRandomNumberGenerator()
// Bool.random(using: &rng)
let randomBool1 = Bool.random()
let randomBool2 = Bool.random(using: &myCustomRandomNumberGenerator)

Collection Additions

Random Element

For Collection we add an extension method for collections to get a random element.

Collection example:

let greetings = ["hey", "hi", "hello", "hola"]
// Utilizes the standard library's default random
// Alias to:
// var rng = SystemRandomNumberGenerator()
// greetings.randomElement(using: &rng)!
print(greetings.randomElement()!) // This returns an Optional
print(greetings.randomElement(using: &myCustomRandomNumberGenerator)!) // This returns an Optional

Note that some types make it easy to form collections with more elements than can be represented as an Int, such as the range Int.min...Int.max, and randomElement will likely trap on such collections. However, such ranges are likely to trap when used with almost any collection API, and the random(in:) method on FixedWidthInteger can be used for this purpose instead.

Shuffle API

As a result of adding the random API, it only makes sense to utilize that power to fuel the shuffle methods. We extend MutableCollection to add a method to shuffle the collection itself, and extend Sequence to add a method to return a shuffled version of itself in a new array. Example:

var greetings = ["hey", "hi", "hello", "hola"]
// Utilizes the standard library's default random
// Alias to:
// var rng = SystemRandomNumberGenerator()
// greetings.shuffle(using: &rng)
greetings.shuffle()
print(greetings) // A possible output could be ["hola", "hello", "hey", "hi"]
let numbers = 0 ..< 5
print(numbers.shuffled(using: &myCustomRandomNumberGenerator)) // A possible output could be [1, 3, 0, 4, 2]
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: