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
AIエージェント時代のコードレビューを設計する
nogu66
6
2.7k
A2UI for Android: Safely Rendering AI-Generated UI with Jetpack Compose - DroidKaigi 2026
itsmedreamwalker
0
140
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
720
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
280
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
4.6k
数年滞っていたダークモード対応をおよそ2週間で完了させる
chigichan24
0
710
XHTMLが残したもの
yosuke_furukawa
PRO
2
810
すこし踏み込む CancellationToken
htkym
0
160
AWS Step Functions 大規模並列の壁を越える / jaws-sonic-2026-niigata-step-functions
kasacchiful
PRO
1
470
[GoCon2026] When Goroutines Are Not Enough: Runtime Locality in High-Throughput Go
takehaya
6
2.3k
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
7.2k
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
210
Featured
See All Featured
Designing for Timeless Needs
cassininazir
1
480
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Writing Fast Ruby
sferik
630
63k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
670
Fireside Chat
paigeccino
43
4k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
570
What does AI have to do with Human Rights?
axbom
PRO
1
2.4k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.8k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
390
ラッコキーワード サービス紹介資料
rakko
1
4.9M
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