Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
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
280
Consuming Web APIs, the TDD way
luisobo
6
380
Techniques to write DSLs in Objective-C
luisobo
9
930
Other Decks in Programming
See All in Programming
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4.2k
act1-costs.pdf
sumedhbala
0
190
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
240
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
160
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
160
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
210
継続モナドとリアクティブプログラミング
yukikurage
3
450
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.9k
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
440
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1k
スマートグラスで並列バイブコーディング
hyshu
0
280
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
Featured
See All Featured
30 Presentation Tips
portentint
PRO
1
340
Automating Front-end Workflow
addyosmani
1370
210k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
2
320
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
63
55k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
180
Why Our Code Smells
bkeepers
PRO
340
58k
WCS-LA-2024
lcolladotor
0
680
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
170
Mobile First: as difficult as doing things right
swwweet
225
10k
Navigating Weather and Climate Data
rabernat
0
280
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
310
Statistics for Hackers
jakevdp
799
230k
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