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

DSLs in Swift

DSLs in Swift

A talk I given at SwiftCrunch http://swiftcrunch.com, the first ever hack day on Swift.

https://github.com/kylef/QueryKit
https://twitter.com/kylefuller

Kyle Fuller

July 06, 2014
Tweet

More Decks by Kyle Fuller

Other Decks in Technology

Transcript

  1. DESIGNING | BUILDING
    DSLS IN SWIFT
    @KYLEFULLER

    View Slide

  2. View Slide

  3. View Slide

  4. BASE

    View Slide

  5. View Slide

  6. SHAPE

    View Slide

  7. WHAT IS A DSL?

    View Slide

  8. DOMAIN SPECIFIC
    LANGUAGE

    View Slide

  9. PROGRAMMING
    LANGUAGE

    View Slide

  10. DECLARATIVE

    View Slide

  11. View Slide

  12. SPECIFIC

    View Slide

  13. NOT GENERIC

    View Slide

  14. FREEDOM

    View Slide

  15. BUGS

    View Slide

  16. NSPredicate

    View Slide

  17. NAME EQUALS Kyle

    View Slide

  18. USING
    NSPredicate

    View Slide

  19. NSExpression *left = [NSExpression expressionForKeyPath:@"name"];
    NSExpression *right = [NSExpression expressionForConstantValue:@"Kyle"];
    [NSComparisonPredicate
    predicateWithLeftExpression:left
    rightExpression:right
    modifier:NSDirectPredicateModifier
    type:NSEqualToPredicateOperatorType
    options:0];

    View Slide

  20. IMPERATIVE

    View Slide

  21. FRUSTRATING

    View Slide

  22. [NSPredicate predicateWithFormat:@"name == Kyle"];

    View Slide

  23. EASY TO WRITE
    BUGS

    View Slide

  24. [NSPredicate predicateWithFormat:@"nme == Kyle"];

    View Slide

  25. NSString *key = NSStringFromSelector(@selector(name));
    [NSPredicate predicateWithFormat:@"%K == Kyle", key];

    View Slide

  26. INELEGANT

    View Slide

  27. DSLS

    View Slide

  28. [[Person name] equals:@"Kyle"]

    View Slide

  29. SWIFT

    View Slide

  30. CUSTOM
    OPERATORS

    View Slide

  31. (Something) == (Something else)

    View Slide

  32. / = - + * % <
    > ! & | ^ . ~

    View Slide

  33. Person.name == "Kyle"

    View Slide

  34. IDEA

    View Slide

  35. CORE DATA
    DSL

    View Slide

  36. MAKING A QUERY IN
    CORE DATA

    View Slide

  37. let fetchRequest = NSFetchRequest(entityName: "Person")
    fetchRequest.predicate = NSPredicate(format:"name == %@", "Kyle")
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
    var error:NSErrorPointer?
    var objects = managedObjectContext.executeFetchRequest(fetchRequest, error:error!)
    if let objects = objects {
    ;
    }

    View Slide

  38. 6 LINES

    View Slide

  39. Person.queryset(context)
    .orderBy(Person.name.ascending)
    .filter(Person.name == "Kyle")

    View Slide

  40. 1 LINE

    View Slide

  41. REFACTORING

    View Slide

  42. class Person : NSManagedObject {
    var name:String?
    - var age:NSNumber?
    }

    View Slide

  43. class Person : NSManagedObject {
    var name:String?
    - var age:NSNumber?
    + var birthday:NSDate?
    }

    View Slide

  44. BUILD
    SUCCESSFUL

    View Slide

  45. View Slide

  46. *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath age not found in entity '
    *** First throw call stack:
    (
    0 CoreFoundation 0x00000001039e5495 __exceptionPreprocess + 165
    1 libobjc.A.dylib 0x0000000102fe199e objc_exception_throw + 43
    2 CoreData 0x0000000100b04438 -[NSSQLGenerator newSQLStatementForFetchRequest:ignoreInheritance:countOnly:nestingLevel:] + 904
    3 CoreData 0x0000000100b03f6d -[NSSQLAdapter _newSelectStatementWithFetchRequest:ignoreInheritance:] + 365
    4 CoreData 0x0000000100b03bd3 -[NSSQLCore newRowsForFetchPlan:] + 115
    5 CoreData 0x0000000100b034a4 -[NSSQLCore objectsForFetchRequest:inContext:] + 516
    6 CoreData 0x0000000100b03025 -[NSSQLCore executeRequest:withContext:error:] + 245
    7 CoreData 0x0000000100b02b60 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 3504
    8 CoreData 0x0000000100b00a21 -[NSManagedObjectContext executeFetchRequest:error:] + 497
    9 Fitness First 0x000000010021d89a -[KFObjectManager array:] + 138
    10 Fitness First 0x00000001000c6396 -[FFGDashboardViewController viewWillAppear:] + 1190
    11 UIKit 0x0000000101ae8db5 -[UIViewController _setViewAppearState:isAnimating:] + 422
    12 UIKit 0x0000000101d42410 -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:animation:] + 5629
    13 UIKit 0x0000000101aeffce -[UIViewController presentViewController:withTransition:completion:] + 4854
    )
    libc++abi.dylib: terminating with uncaught exception of type NSException

    View Slide

  47. Keypath age not found in entity

    View Slide

  48. class Person : NSManagedObject {
    var name:String?
    - var age:NSNumber?
    + var birthday:NSDate?
    }

    View Slide

  49. NSSortDescriptor(key: "age", ascending: true)

    View Slide

  50. COULD WE DO THIS
    BETTER
    WITH A DSL?

    View Slide

  51. SAY NO TO HARD
    CODED STRINGS

    View Slide

  52. Person.age.ascending

    View Slide

  53. READABILITY

    View Slide

  54. SAFE

    View Slide

  55. View Slide

  56. DSLS ARE
    !

    View Slide

  57. !

    View Slide

  58. DON'T ABUSE THE POWER
    Swift HAS GIVEN US.

    View Slide

  59. GOING TOO FAR

    View Slide

  60. BAD

    View Slide

  61. Person.name %= "Kyle"

    View Slide

  62. CONSIDERATION

    View Slide

  63. INTUITIVE

    View Slide

  64. >>> Person.age > 27
    Age is more than 27

    View Slide

  65. >>> Person.name %= "Kyle"
    Name is ? Kyle

    View Slide

  66. >>> Person.name ~= "Kyle"
    Name is LIKE Kyle

    View Slide

  67. WHERE TO
    START?

    View Slide

  68. README

    View Slide

  69. View Slide

  70. README

    View Slide

  71. PLAYGROUNDS

    View Slide

  72. QuerySet().filter(Person.name == "Kyle")
    .orderBy(Person.name.ascending)

    View Slide

  73. View Slide

  74. class Attribute {
    var ascending:NSSortDescriptor {
    return NSSortDescriptor(key: "x", ascending: true)
    }
    var descending:NSSortDescriptor {
    return NSSortDescriptor(key: "x", ascending: true)
    }
    }
    @infix func == (left: Attribute, right: AnyObject) -> NSPredicate {
    return NSPredicate(format: "x", nil)
    }

    View Slide

  75. class QuerySet {
    func orderBy(sortDescriptor:NSSortDescriptor) -> QuerySet {
    return self
    }
    func filter(predicate:NSPredicate) -> QuerySet {
    return self
    }
    subscript(range: Range) -> QuerySet {
    return self
    }
    }

    View Slide

  76. class Person {
    class var name:Attribute {
    return Attribute()
    }
    }
    QuerySet().filter(Person.name == "Kyle")
    .orderBy(Person.name.ascending)

    View Slide

  77. !

    View Slide

  78. WHAT'S NEXT?

    View Slide

  79. WRITING THE
    REAL
    IMPLEMENTATION

    View Slide

  80. View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. View Slide

  85. View Slide

  86. 04:00 AM

    View Slide

  87. View Slide

  88. View Slide

  89. View Slide

  90. View Slide

  91. github.com/kylef/QueryKit

    View Slide

  92. twitter.com/kylefuller

    View Slide

  93. View Slide

  94. github.com/kylef/QueryKit
    twitter.com/kylefuller

    View Slide