Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Techniques to write DSLs in Swift
Search
Luis Solano
June 06, 2014
Programming
1.5k
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Techniques to write DSLs in Swift
Luis Solano
June 06, 2014
More Decks by Luis Solano
See All by Luis Solano
How I Spent All My Savings Reinventing Programming
luisobo
0
290
Consuming Web APIs, the TDD way
luisobo
6
390
Techniques to write DSLs in Objective-C
luisobo
9
950
Other Decks in Programming
See All in Programming
残高管理から台帳サービスへの進化
artoy
0
140
Verilogで学ぶCPU自作入門.pdf
uyuki234
8
4k
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
530
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.4k
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
180
自サークルのIdPからログアウトできるようになるまで
taiseiue
0
100
mrbgem 三角測量 開発
ogom
0
200
すこし踏み込む CancellationToken
htkym
2
1.6k
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
390
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
100
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
580
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
20
21k
Featured
See All Featured
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
510
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Test your architecture with Archunit
thirion
2
2.4k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
300
Designing for Performance
lara
611
70k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
350
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.7k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Transcript
“Clean code reads like well-written prose” – Grady Booch
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
@implementation Subscription - (id)init { self = [super init]; if
(!self) return nil; self.state = @"pending"; } - (void)activateWithCode:(NSString *)activationCode { if ([self.state isEqualToString:@"pending"] && [self.code isEqualToString:activationCode]) { self.state = @"active"; } } - (void)suspend { if ([self.state isEqualToString:@"active"]) { [self stopBilling]; } } @end IMPLEMENTATION
LSStateMachine *sm = [[LSStateMachine alloc] init]; LSState *s1 = [[LSState
alloc] initWithName:@"pending"]; LSState *s2 = [[LSState alloc] initWithName:@"active"]; [sm addState:s1]; [sm addState:s2]; LSEvent *e1 = [[LSEvent alloc] initWithName:@"activate"]; LSTransition *t1 = [[LSTransition alloc] initWithEvent:e1 from:s1 to:s2]; [sm addTransition:t1]; ABSTRACTION
TECHNIQUES TO WRITE DSLs IN OBJECTIVE-C
TECHNIQUES TO WRITE DSLs IN SWIFT
LUIS SOLANO @luisobo
DOMAIN-SPECIFIC LANGUAGE
LANGUAGE PROGRAMMING
SPECIFIC CONSTRAINT
TO A CERTAIN DOMAIN CERTAIN TYPE OF PROBLEM
LSStateMachine *sm = [[LSStateMachine alloc] init]; LSState *s1 = [[LSState
alloc] initWithName:@"pending"]; LSState *s2 = [[LSState alloc] initWithName:@"active"]; [sm addState:s1]; [sm addState:s2]; LSEvent *e1 = [[LSEvent alloc] initWithName:@"activate"]; LSTransition *t1 = [[LSTransition alloc] initWithEvent:e1 from:s1 to:s2]; [sm addTransition:t1]; ABSTRACTION
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
LSStateMachine *sm = [[LSStateMachine alloc] init]; LSState *s1 = [[LSState
alloc] initWithName:@"pending"]; LSState *s2 = [[LSState alloc] initWithName:@"active"]; [sm addState:s1]; [sm addState:s2]; LSEvent *e1 = [[LSEvent alloc] initWithName:@"activate"]; LSTransition *t1 = [[LSTransition alloc] initWithEvent:e1 from:s1 to:s2]; [sm addTransition:t1]; ABSTRACTION
sm.when("activate", transitionFrom: "pending", to: "active") DSL
DSL luisobo/StateMachine
sm.when("activate", transitionFrom: "pending", to: "active") sm.when("suspend", transitionFrom: "active", to: "suspended")
sm.when("unsuspend", transitionFrom: "suspended", to: "active") sm.when("terminate", transitionFrom: "active", to: "terminated") sm.when("terminate", transitionFrom: "suspended", to: "terminated") ! sm.before("terminate") { subscription in subscription.terminatedAt = NSDate.date() } ! sm.before("suspend") { subscription in subscription.stopBilling() } DSL
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla
asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla asjdañlksjdlaksjdñlak asjdañlksjdla Semantic model
Domain-specfic language
DECLARATIVE
imperative
imperative is fine
imperative works
my brain
my brain cannot handle imperative programming’s scale
DECLARATIVE
INTERNAL EXTERNAL INTERNAL
DSL? WHY TO
DSL? WHEN TO
DSL? HOW TO
SWIFT METHODS luisobo/StateMachine sm.when("activate", transitionFrom: "pending", to: "active")
LSStateMachine *sm = [[LSStateMachine alloc] init]; LSState *s1 = [[LSState
alloc] initWithName:@"pending"]; LSState *s2 = [[LSState alloc] initWithName:@"active"]; [sm addState:s1]; [sm addState:s2]; LSEvent *e1 = [[LSEvent alloc] initWithName:@"activate"]; LSTransition *t1 = [[LSTransition alloc] initWithEvent:e1 from:s1 to:s2]; [sm addTransition:t1]; SWIFT METHODS luisobo/StateMachine
SWIFT METHODS luisobo/StateMachine sm.when("activate", transitionFrom: "pending", to: "active")
METHOD CHAINING stubRequest("GET", "http://hello.com/"). andReturn(200).withBody("hola"); luisobo/Nocilla
stubRequest("POST", "http://hello.com"). withHeader("Content-Type", "text/plain"). withHeader("X-MY-AWESOME-HEADER", "sisisi"). withBody("Adios!"). andReturn(200). withHeader("Content-Type", "text/plain").
withBody("hola"); METHOD CHAINING luisobo/Nocilla
EXTENSIONS state.should.eq(“active”)
EXTENSIONS extension NSObject { var should:Matcher { return Matcher() }
}
[1, 2, 3] [1: “one”, 2: “two”, 3: “three”] ARRAY
AND DICTIONARY LITERALS
stubRequest("GET", “http://hello.com"). withHeaders([“Content-Type": “text/plain", "X-MY-AWESOME-HEADER" : “YEAH!”]). andReturn(200); luisobo/Nocilla ARRAY
AND DICTIONARY LITERALS
.adjustTemperature(24.5).at(“nine"). USING LITERALS WITH YOUR TYPES
func adjustTemperature(to: Temp) -> Thermostat func at(time: NSDate) -> Thermostat
USING LITERALS WITH YOUR TYPES
protocol FloatLiteralConvertible { ! typealias FloatLiteralType ! class func convertFromFloatLiteral(value:
FloatLiteralType) -> Self ! } USING LITERALS WITH YOUR TYPES
extension Temp: FloatLiteralConvertible { typealias FloatLiteralType = Double class func
convertFromFloatLiteral(value: Double) -> Temp { return Temp(degrees: value) } } USING LITERALS WITH YOUR TYPES
IntegerLiteralConvertible FloatLiteralConvertible StringLiteralConvertible LogicValue StringInterpolationConvertible ArrayLiteralConvertible DictionaryLiteralConvertible USING LITERALS WITH
YOUR TYPES 1 3.5 “foo” if myValue “hello \(value)” [1, 2, 3] [“one”: 1, “two”: 2]
BLOCKS sm.before("terminate") { subscription in subscription.terminatedAt = NSDate.date() } luisobo/StateMachine
BLOCKS FOR CONTEXT User.defineFactory { f in f["foo"] = "bar"
f["password"] = "hunter2" } luisobo/Defactory
SYNTACTIC SUGAR o.com”).with.body(“Hola”).and.return(200) luisobo/Nocilla
OPERATOR OVERLOAD ^= *= * % + - / <<=
||= << || |= <= >>= !== >= ~= | >> < += &&= %= ... -= &% /= && &* &+ &- &/ === == & .. ^ > &= != ~ ! -- ++ - + -- ++
OPERATOR OVERLOAD let s: Speed = 100.meters / 5.seconds
CUSTOM OPERATORS / = - + * % < >
! & | ^ . ~.
CUSTOM OPERATORS operator infix =>> {}
=>>
stubRequest("GET","http://hello.com/") luisobo/Nocilla stubRequest(method:String, url:String)
stubRequest("GET", "http://hello.com/") stubRequest(“GET", regex(“http://.*\.com”)) luisobo/Nocilla stubRequest(method:String, url:AnyObject)
stubRequest("POST", NSObject()); stubRequest(“POST”, []); luisobo/Nocilla stubRequest(method:String, url:AnyObject)
protocol Matchable { func matcher() -> Matcher } ADD PROTOCOL
TO EXISTING CLASS luisobo/Nocilla
extension NSString: Matchable { func matcher() -> Matcher { return
StringMatcher(pattern: self) } } ADD PROTOCOL TO EXISTING CLASS
ADD PROTOCOL TO EXISTING CLASS extension NSRegularExpression: Matchable { func
matcher() -> Matcher { return RegexMatcher(pattern: self) } }
ADD PROTOCOL TO EXISTING CLASS stubRequest("GET", "http://hello.com/") stubRequest(“GET", regex(“http://.*\.com”)) luisobo/Nocilla
stubRequest(method:String, url:Matcheable)
stubRequest("POST", NSObject()]); stubRequest(“POST”,[]]); stubRequest(method:String, url:Matcheable) ADD PROTOCOL TO EXISTING CLASS
luisobo/Nocilla
None
Thin layer on top of an abstraction. Limited expressiveness and
domain focus. Make your code more maintainable Developer happiness Improved communication Fewer bugs! As seen in TV
! ! Domain-specific languages by Martin Fowler brain photo: flickr/_DJ_
redacted font: github/christiannaths Nocilla: github/luisobo StateMachine: github/luisobo Defactory: github/luisobo Kiwi: github/kiwi-bdd metamacros.h github/jspahrsummers THANKS! @luisobo