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

Flexible Code for Generic Collections

5f463dd4e7af28b64ad8f0e032ee82dc?s=47 Nate Cook
November 07, 2016

Flexible Code for Generic Collections

Presented at Swift Summit on 11/7/2016

5f463dd4e7af28b64ad8f0e032ee82dc?s=128

Nate Cook

November 07, 2016
Tweet

Transcript

  1. Flexible Code for Generic Collections Nate Cook @nnnnnnnn

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

  3. Protocols in Objective-C

  4. Protocols in Swift

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

    it
  6. struct Brick { let color: UIColor let size: (w: Int,

    h: Int) }
  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))
  8. struct Brick { let color: UIColor let size: (w: Int,

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

    h: Int) } let bricks = [ , , , , , , ]
  10. func createBrickPDF(_ bricks: [Brick]) -> Data { let result =

    NSMutableData() // prepare PDF context for brick in bricks { // generate PDF data } return result as Data }
  11. let bricks = [ , , , , , ,

    ] let pdfData = createBrickPDF(bricks) // Success!
  12. !"#

  13. let bricks = [ , , , , , ,

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

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

    ] let firstFiveBricks = bricks.prefix(5) let pdfData = createBrickPDF(firstFiveBricks) Cannot convert value of type ‘ArraySlice<Brick>’ to expected argument type ‘[Brick]’ !
  16. $%&

  17. let bricks = [ , , , , , ,

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

    convert value of type ‘ArraySlice<Brick>’ to expected argum
  19. DF(firstFiveBrick type ‘ArraySlice<Brick>’ to exp

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

  21. let bricks = [ , , , , , ,

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

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

    ] let firstFiveBricks = bricks.prefix(5) let pdfData = createBrickPDF(Array(firstFiveBricks)) // '☕
  24. 1. A problem 2.Why it happens 3.What to do about

    it
  25. let middleBricks = bricks[2..<5] // ArraySlice<Brick> let lastThreeBricks = bricks.suffix(3)

    // ArraySlice<Brick> let bricksAfterFirstTwo = bricks.dropFirst(2) // ArraySlice<Brick>
  26. let backwardsBricks = bricks.reversed() // ReversedRandomAccessCollection<[Brick]> let squareBrickColors = bricks.lazy

    .filter { $0.isSquare } .map { $0.color } // LazyMapBidirectionalCollection // <LazyFilterBidirectionalCollection<[Brick]>, UIColor>
  27. Collection wrappers

  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
  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
  30. Brought to you by Brick-o-Vision™

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

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

  33. let firstFiveBricks = bricks.prefix(5)

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

    it
  35. Collection protocols

  36. Sequence Collection MutableCollection RangeReplaceableCollection BidirectionalCollection Array ArraySlice RandomAccessCollection

  37. Sequence Collection MutableCollection RangeReplaceableCollection BidirectionalCollection Array ArraySlice RandomAccessCollection

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

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

    , , , , , ,
  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
  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)
  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
  43. Sequence Collection MutableCollection RangeReplaceableCollection BidirectionalCollection Array ArraySlice RandomAccessCollection

  44. Collection protocol bricks[1] // if let i = bricks.index(where: {

    $0.color == .blue }) { print(i, bricks[i]) } // 4, bricks.count // 7
  45. Sequence Collection MutableCollection RangeReplaceableCollection BidirectionalCollection Array ArraySlice RandomAccessCollection

  46. Sequence Collection MutableCollection RangeReplaceableCollection BidirectionalCollection Array ArraySlice RandomAccessCollection IteratorProtocol

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

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

    } // , , , , , , var iterator = bricks.makeIterator() while let brick = iterator.next() { print(brick) } // , , , , , ,
  49. func createBrickPDF(_ bricks: [Brick]) -> Data { let result =

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

    NSMutableData() // prepare PDF context for brick in bricks { // generate PDF data } return result as Data }
  51. func createBrickPDF<S: Sequence>(_ 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 }
  52. func createBrickPDF<S: Sequence>(_ 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 }
  53. func createBrickPDF<S: Sequence>(_ 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 }
  54. Value of type ’S’ has no member ‘count’ ! func

    createBrickPDF<S: Sequence>(_ 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 }
  55. func createBrickPDF<C: Collection>(_ 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 }
  56. func createBrickPDF<S: Sequence>(_ bricks: S) -> Data where S.Iterator.Element ==

    Brick { ... } let bricks = [ , , , , , , ] let firstFiveBricks = bricks.prefix(5) let pdfData = createBrickPDF(firstFiveBricks) // Success!!
  57. !"#❤*✨!" ❤*✨!"#❤* ✨!"#❤*✨! "#❤*✨!"#

  58. Storing generic collections

  59. Generic type class BrickRenderer<C: Collection> where C.Iterator.Element == Brick {

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

    AnyCollection<Brick>) { self.bricks = bricks } }
  61. Type erasure class BrickRenderer { let bricks: AnyCollection<Brick> init(_ bricks:

    AnyCollection<Brick>) { self.bricks = bricks } } let renderer = BrickRenderer(AnyCollection(bricks)) let renderer = BrickRenderer(AnyCollection(firstFiveBricks))
  62. Type-specific extensions

  63. extension Array where Element == Bool { func allTrue() ->

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

    Bool { for x in self { if !x { return false } } return true } }
  66. Syntax & Naming

  67. func createBrickPDF<S: Sequence>(_ bricks: S) -> Data where S.Iterator.Element ==

    Brick Swift 3 func createBrickPDF<S: SequenceType where S.Generator.Element == Brick> (_ bricks: S) -> NSData Swift 2
  68. Thanks! "@" + repeatElement("n", count: 8).joined()