Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Flexible Code for Generic Collections

Nate Cook
November 07, 2016

Flexible Code for Generic Collections

Presented at Swift Summit on 11/7/2016

Nate Cook

November 07, 2016
Tweet

More Decks by Nate Cook

Other Decks in Programming

Transcript

  1. Flexible Code for
    Generic Collections
    Nate Cook @nnnnnnnn

    View Slide

  2. The standard library
    collections system
    (Nate’s favorite part of Swift)

    View Slide

  3. Protocols in
    Objective-C

    View Slide

  4. Protocols in
    Swift

    View Slide

  5. 1. A problem
    2.Why it happens
    3.What to do about it

    View Slide

  6. struct Brick {
    let color: UIColor
    let size: (w: Int, h: Int)
    }

    View Slide

  7. struct Brick {
    let color: UIColor
    let size: (w: Int, h: Int)
    }
    let redSquare = Brick(color: .red, size: (2, 2))
    let orangeSquare = Brick(color: .orange, size: (2, 2))

    View Slide

  8. struct Brick {
    let color: UIColor
    let size: (w: Int, h: Int)
    }
    let redSquare =
    let orangeSquare =

    View Slide

  9. struct Brick {
    let color: UIColor
    let size: (w: Int, h: Int)
    }
    let bricks = [ , , , , , , ]

    View Slide

  10. func createBrickPDF(_ bricks: [Brick]) -> Data {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  11. let bricks = [ , , , , , , ]
    let pdfData = createBrickPDF(bricks)
    // Success!

    View Slide

  12. !"#

    View Slide

  13. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)

    View Slide

  14. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    Wow
    such swift
    so declarativ
    wow

    View Slide

  15. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(firstFiveBricks)
    Cannot convert value of type ‘ArraySlice’ to expected argument type ‘[Brick]’
    !

    View Slide

  16. $%&

    View Slide

  17. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(firstFiveBricks)
    Cannot convert value of type ‘ArraySlice’ to expected argument type ‘[Brick]’
    !

    View Slide

  18. , , , , , ]
    ks = bricks.prefix(5)
    ateBrickPDF(firstFiveBricks)
    convert value of type ‘ArraySlice’ to expected argum

    View Slide

  19. DF(firstFiveBrick
    type ‘ArraySlice’ to exp

    View Slide

  20. (˽°□°)˽
    Ɨ ʇɟıʍs

    View Slide

  21. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(firstFiveBricks)
    Cannot convert value of type ‘ArraySlice’ to expected argument type ‘[Brick]’
    !

    View Slide

  22. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(Array(firstFiveBricks))

    View Slide

  23. let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(Array(firstFiveBricks))
    // '☕

    View Slide

  24. 1. A problem
    2.Why it happens
    3.What to do about it

    View Slide

  25. let middleBricks = bricks[2..<5]
    // ArraySlice
    let lastThreeBricks = bricks.suffix(3)
    // ArraySlice
    let bricksAfterFirstTwo = bricks.dropFirst(2)
    // ArraySlice

    View Slide

  26. let backwardsBricks = bricks.reversed()
    // ReversedRandomAccessCollection<[Brick]>
    let squareBrickColors = bricks.lazy
    .filter { $0.isSquare }
    .map { $0.color }
    // LazyMapBidirectionalCollection
    // , UIColor>

    View Slide

  27. Collection
    wrappers

    View Slide

  28. LazySequence MutableBidirectionalSlice LazyBidirectionalCollection
    ReversedRandomAccessCollection EnumeratedSequence FlattenCollection
    LazyMapSequence LazyFilterBidirectionalCollection AnyBidirectionalCollection
    MutableRangeReplaceableBidirectionalSlice LazyMapBidirectionalCollection
    LazyCollection BidirectionalSlice AnyRandomAccessCollection ArraySlice
    FlattenBidirectionalCollection Zip2Sequence MutableSlice AnyCollection
    AnySequence RangeReplaceableBidirectionalSlice JoinedSequence
    LazyMapRandomAccessCollection LazyFilterCollection LazyMapCollection
    MutableRangeReplaceableRandomAccessSlice RandomAccessSlice
    RangeReplaceableSlice LazyFilterSequence LazyRandomAccessCollection
    MutableRangeReplaceableSlice RangeReplaceableRandomAccessSlice
    FlattenSequence ReversedCollection MutableRandomAccessSlice

    View Slide

  29. LazySequence MutableBidirectionalSlice LazyBidirectionalCollection
    ReversedRandomAccessCollection EnumeratedSequence FlattenCollection
    LazyMapSequence LazyFilterBidirectionalCollection AnyBidirectionalCollection
    MutableRangeReplaceableBidirectionalSlice LazyMapBidirectionalCollection
    LazyCollection BidirectionalSlice AnyRandomAccessCollection ArraySlice
    FlattenBidirectionalCollection Zip2Sequence MutableSlice AnyCollection
    AnySequence RangeReplaceableBidirectionalSlice JoinedSequence
    LazyMapRandomAccessCollection LazyFilterCollection LazyMapCollection
    MutableRangeReplaceableRandomAccessSlice RandomAccessSlice
    RangeReplaceableSlice LazyFilterSequence LazyRandomAccessCollection
    MutableRangeReplaceableSlice RangeReplaceableRandomAccessSlice
    FlattenSequence ReversedCollection MutableRandomAccessSlice

    View Slide

  30. Brought to you by
    Brick-o-Vision™

    View Slide

  31. let bricks = [ , , , , , , ]

    View Slide

  32. let bricksCopy = Array(bricks.prefix(5))

    View Slide

  33. let firstFiveBricks = bricks.prefix(5)

    View Slide

  34. 1. A problem
    2.Why it happens
    3.What to do about it

    View Slide

  35. Collection
    protocols

    View Slide

  36. Sequence
    Collection
    MutableCollection RangeReplaceableCollection
    BidirectionalCollection
    Array ArraySlice
    RandomAccessCollection

    View Slide

  37. Sequence
    Collection
    MutableCollection RangeReplaceableCollection
    BidirectionalCollection
    Array ArraySlice
    RandomAccessCollection

    View Slide

  38. A series of values
    we access one at a time

    View Slide

  39. Sequence protocol
    for brick in bricks {
    print(brick)
    }
    // , , , , , ,

    View Slide

  40. Sequence protocol
    for brick in bricks.prefix(5) {
    print(brick)
    }
    // , , , ,
    for number in 1..<10 {
    print(number)
    }
    // 1, 2, 3, 4, 5, 6, 7, 8, 9

    View Slide

  41. Sequence protocol
    bricks.contains(where: { $0.color == .blue })
    // true
    bricks.first(where: { $0.color == .blue })
    // Optional( )
    [2, 4, 5, 3, 7, 2, 9].max()
    // Optional(9)

    View Slide

  42. Sequence protocol
    (1..<10).map({ "\($0)" })
    // ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
    (1..<10).filter({ $0 % 2 == 0 })
    // [2, 4, 6, 8]
    (1..<10).reduce(0, +)
    // 45

    View Slide

  43. Sequence
    Collection
    MutableCollection RangeReplaceableCollection
    BidirectionalCollection
    Array ArraySlice
    RandomAccessCollection

    View Slide

  44. Collection protocol
    bricks[1]
    //
    if let i = bricks.index(where: { $0.color == .blue }) {
    print(i, bricks[i])
    }
    // 4,
    bricks.count
    // 7

    View Slide

  45. Sequence
    Collection
    MutableCollection RangeReplaceableCollection
    BidirectionalCollection
    Array ArraySlice
    RandomAccessCollection

    View Slide

  46. Sequence
    Collection
    MutableCollection RangeReplaceableCollection
    BidirectionalCollection
    Array ArraySlice
    RandomAccessCollection
    IteratorProtocol

    View Slide

  47. Iterator of a sequence
    for brick in bricks {
    print(brick)
    }
    // , , , , , ,

    View Slide

  48. Iterator of a sequence
    for brick in bricks {
    print(brick)
    }
    // , , , , , ,
    var iterator = bricks.makeIterator()
    while let brick = iterator.next() {
    print(brick)
    }
    // , , , , , ,

    View Slide

  49. func createBrickPDF(_ bricks: [Brick]) -> Data {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  50. func createBrickPDF(_ bricks: [Brick]) -> Data {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  51. func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  52. func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  53. func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    {
    let result = NSMutableData()
    // prepare PDF context
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  54. Value of type ’S’ has no member ‘count’
    !
    func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    {
    let result = NSMutableData()
    // prepare PDF context
    let renderer = BrickPDFRenderer(count: bricks.count)
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  55. func createBrickPDF(_ bricks: C) -> Data
    where C.Iterator.Element == Brick
    {
    let result = NSMutableData()
    // prepare PDF context
    let renderer = BrickPDFRenderer(count: bricks.count)
    for brick in bricks {
    // generate PDF data
    }
    return result as Data
    }

    View Slide

  56. func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    { ... }
    let bricks = [ , , , , , , ]
    let firstFiveBricks = bricks.prefix(5)
    let pdfData = createBrickPDF(firstFiveBricks)
    // Success!!

    View Slide

  57. !"#❤*✨!"
    ❤*✨!"#❤*
    ✨!"#❤*✨!
    "#❤*✨!"#

    View Slide

  58. Storing generic
    collections

    View Slide

  59. Generic type
    class BrickRenderer
    where C.Iterator.Element == Brick
    {
    let bricks: C
    init(_ bricks: C) {
    self.bricks = bricks
    }
    }

    View Slide

  60. Type erasure
    class BrickRenderer {
    let bricks: AnyCollection
    init(_ bricks: AnyCollection) {
    self.bricks = bricks
    }
    }

    View Slide

  61. Type erasure
    class BrickRenderer {
    let bricks: AnyCollection
    init(_ bricks: AnyCollection) {
    self.bricks = bricks
    }
    }
    let renderer = BrickRenderer(AnyCollection(bricks))
    let renderer =
    BrickRenderer(AnyCollection(firstFiveBricks))

    View Slide

  62. Type-specific
    extensions

    View Slide

  63. extension Array where Element == Bool {
    func allTrue() -> Bool {
    for x in self {
    if !x {
    return false
    }
    }
    return true
    }
    }

    View Slide

  64. extension Array where Element == Bool {
    func allTrue() -> Bool {
    for x in self {
    if !x {
    return false
    }
    }
    return true
    }
    }
    Same-type requirement makes generic parameter 'Element' non-generic
    !

    View Slide

  65. extension Sequence where Iterator.Element == Bool {
    func allTrue() -> Bool {
    for x in self {
    if !x {
    return false
    }
    }
    return true
    }
    }

    View Slide

  66. Syntax & Naming

    View Slide

  67. func createBrickPDF(_ bricks: S) -> Data
    where S.Iterator.Element == Brick
    Swift 3
    func createBrickPDFwhere S.Generator.Element == Brick>
    (_ bricks: S) -> NSData
    Swift 2

    View Slide

  68. Thanks!
    "@" + repeatElement("n", count: 8).joined()

    View Slide