SLIDE 1 Swi$:&New&Paradigms&for& iOS&Development
Marc%Prud'hommeaux
Indie&So)ware&Developer&/&marc@impathic.com
GOTO$Conference
Copenhagen,*September*25,*2014
1
SLIDE 2 Overview
- 1. Swi&
- 2. FP
- 3. Tweet
- 4. HOF
- 5. Bork
- 6. Ques:ons?
2
SLIDE 3 Flash&Poll
- Who%Programs?
- Who%Programs%in%Objec4ve6C?
- Who%Programs%in%Java/C#/C++?
- Who%Programs%Func4onally?
- Who%Programs%in%SwiB?
3
SLIDE 4
Tweet!
4
SLIDE 5 @interface Tweet : NSObject // Tweet.h @property (nonatomic, strong) NSString *username; @property (nonatomic, strong) NSString *body;
- (instancetype)initWithUsername:(NSString *)username body:(NSString *)body;
@end @implementation Tweet // Tweet.m
- (instancetype)initWithUsername:(NSString *)username
body:(NSString *)body { if (self = [super init]) { self.username = username; self.body = body; } return self; } @end @interface TweeterService : NSObject
- (void)tweet:(Tweet *)tweet;
+ (TweeterService *)sharedService; @end TweeterService *service = [TweeterService sharedService]; Tweet *tweet = [[Tweet alloc] initWithUsername:@"gotocph" body:@"Come see my talk at GOTO!"]; [service tweet:tweet];
5
SLIDE 6
OBJ$C
meh
6
SLIDE 7 struct Tweet { var username: String let body: String } class TweeterService { func tweet(tweet: Tweet) { ... } class func sharedService() -> TweeterService { ... } } let service = TweeterService.sharedService() let message = "Come see my talk at GOTO!" var tweet = Tweet(username: "gotocph", body: message) service.tweet(tweet)
7
SLIDE 8 Swi$%&%ObjC:%The%Similari3es
- Classes
- Methods
- Protocols/(interfaces)
- Extensions/(categories)
- Func:ons/(methods)
- Semi=automa:c/memory/management/(ARC)
- Closures/(blocks)
8
SLIDE 9 Swi$:&Small&Addi,ons
- Swi%&Structures
- Namespaces
- Swi%&Constants
- Operator&Overloading
- ObjC&Interop
9
SLIDE 10 Swi$:&Big&Addi+ons
- Swi%&Enumera.ons
- Op.onals&(non5nullable&proper.es)
- Generics
- Type&Inference
- Immutability&Support
- Tuples
- First5class&Func.ons
10
SLIDE 11 Swi$:&Big&Addi+ons
- Swi%&Enumera.ons
- Op.onals&(non5nullable&proper.es)
- Generics
- Type&Inference
- Immutability&Support
- Tuples
- First&class*Func-ons
11
SLIDE 12 struct Tweet { var username: String let body: String } class TweeterService { func tweet(tweet: Tweet) { ... } class func sharedService() -> TweeterService { ... } } let service = TweeterService.sharedService() let message = "Come see my talk at GOTO!" var tweet = Tweet(username: "gotocph", body: message) service.tweet(tweet)
12
SLIDE 13 Bug:%No%comple-on%no-fica-on
class TweeterService { func tweet(tweet: Tweet) { ... } }
13
SLIDE 14 Bug:%No%comple-on%no-fica-on
class TweeterService { func tweet(tweet: Tweet) -> Void { ... } }
14
SLIDE 15 Change'the'return'type
class TweeterService { func tweet(tweet: Tweet) -> Bool { ... } }
15
SLIDE 16 Buzzzz!%Wrong!
class TweeterService { func tweet(tweet: Tweet) -> Bool { ... } }
16
SLIDE 17 The$Delegate$Pa+ern$(ObjC)
@protocol TweetDelegate
- (void)tweet:(Tweet *)tweet completed:(BOOL)successful;
@end @interface TweeterService : NSObject @property (weak) id<TweetDelegate> serviceDelegate;
- (void)tweet:(Tweet *)tweet;
+ (TweeterService *)sharedService; @end TweeterService *service = [TweeterService sharedService]; id<TweetDelegate> delegate = [[MyTweetDelegate alloc] init]; service.serviceDelegate = delegate; [service tweet:tweet];
17
SLIDE 18 The$Delegate$Pa+ern$(Swi2)
protocol TweeterDeleate { func tweetCompleted(tweet: Tweet, success: Bool) } class TweeterService { var delegate: TweeterDelegate? func tweet(tweet: Tweet) { ... } class func sharedService() -> TweeterService { ... } } let service = TweeterService.sharedService() let serviceDelegate = MyTweeterDeleate() service.delegate = serviceDelegate service.tweet(tweet)
18
SLIDE 19
FP
19
SLIDE 20 FP
“Func&onal*Programming”
20
SLIDE 21 FP
=>#First)Class#Func0ons
21
SLIDE 22 Func%onal)Programming)is)a) Style
Swi$%has%many%features%that%enable%programming%in% the%Func6onal%Style
A"Func'onal"Language" compels"Func'onal" Programming
Swi$%is%not%really%a%Func2onal%Language
22
SLIDE 23 Swi$%Closures
class TweeterService { func tweet(tweet: Tweet, done: (Bool)->Void) }
23
SLIDE 24 The$Func)on$as$a$Data$Type
class TweeterService { func tweet(tweet: Tweet, done: (Bool)->Void) } var donefun: (Bool)->Void donefun = { success in if success { println("Tweet successful!") } else { println("Tweet failed!") } } service.tweet(tweet, done: donefun)
24
SLIDE 25 Defining'the'Func-on'Inline
class TweeterService { func tweet(tweet: Tweet, done: (success: Bool)->Void) } service.tweet(tweet, done: { (success: Bool) in if success { println("Tweet successful!") } else { println("Fail Whale") } })
25
SLIDE 26 And$More$Succinctly...
class TweeterService { func tweet(tweet: Tweet, done: (success: Bool)->Void) } service.tweet(tweet) { println($0 ? "Tweet successful!" : "Fail Whale") }
26
SLIDE 27 HOF
“Higher(Order(Func/on”
27
SLIDE 28 MAP
FILTER REDUCE
28
SLIDE 29 MAP
Transform)some)Stuff)into)other)Stuff
FILTER
Turn%some%Stuff%into%fewer%Stuff
REDUCE
Turn%some%Stuff%into%a%single%Thing
29
SLIDE 30 A"Danish"Tweet2
let danishTweet = Tweet(username: "Hamlet", body: "Tweets, tweets, tweets") service.tweet(danishTweet, done: { (success) -> Void in if success { println("Be") } else { println("Not to be") } })
2"William"Shakespeare,"Hamlet,"Act"II,"Scene"2"(paraphrased)
30
SLIDE 31 let adviceTweet = Tweet(username: "Polonius", body: "Yet here, Laertes? Aboard, aboard, for shame! " + "The wind sits in the shoulder of your sail " + "And you are stayed for. There, my blessing with thee. " + "And these few precepts in thy memory " + "Look thou character. Give thy thoughts no tongue, " + "Nor any unproportioned thought his act. " + "Be thou familiar but by no means vulgar. " + "Those friends thou hast, and their adoption tried, " + "Grapple them unto thy soul with hoops of steel, " + "But do not dull thy palm with entertainment " + "Of each new-hatched, unfledged comrade. Beware " + "Of entrance to a quarrel, but being in, " + "Bear ’t that th' opposèd may beware of thee. " + "Give every man thy ear but few thy voice. " + "Take each man’s censure but reserve thy judgment. " + "Costly thy habit as thy purse can buy, " + "But not expressed in fancy—rich, not gaudy, " + "For the apparel oft proclaims the man, " + "And they in France of the best rank and station " + "Are of a most select and generous chief in that. " + "Neither a borrower nor a lender be, " + "For loan oft loses both itself and friend, " + "And borrowing dulls the edge of husbandry. " + "This above all: to thine own self be true, " + "And it must follow, as the night the day, " + "Thou canst not then be false to any man. " + "Farewell. My blessing season this in thee.")
31
SLIDE 33 Business'Plan:
- 1. Localized,Tweet,Compression
- 2. ?
- 3. Profit!
33
SLIDE 34 The$Problem$Domain
let charCount = countElements(tweet.body)
=="1,187"characters
countElements()-is-a-global-func3on-in-Swi6
34
SLIDE 35 Transla'ng)the)Tweet
35
SLIDE 36 The$Swedish$Chef
36
SLIDE 37
Börk
37
SLIDE 38
Børk
38
SLIDE 39
Börk Børk
HØF
39
SLIDE 40 Split
let words: [String] = split(tweet.body, { (c: Character) in c == " " })
let words = split(tweet.body, { $0 == " " })
40
SLIDE 41 Words,'words,'words
words = ["Yet", "here", "Laertes?", "Aboard", "aboard", "for", "shame!", "The", "wind", "sits", "in", "the", "shoulder", "of", "your", "sail", "And", "you", "are", "stayed", "for", "There", "my", "blessing", "with", "thee", "And", "these", "few", "precepts", "in", "thy", "memory", "Look", "thou", "character", "Give", "thy", "thoughts", "no", "tongue", "Nor", "any", "unproportioned",
41
SLIDE 42 Filter
let bigWords = words.filter({ (word: String) in countElements(word) > 7 })
More%Compactly:
let bigWords = words.filter({ countElements($0) > 7 })
42
SLIDE 43 Map
let borks: [String] = bigWords.map({ (word: String) in "Børk" })
More%Compactly:
let borks = bigWords.map({ word in "Børk" })
43
SLIDE 44 Reduce
let translation: String = borks.reduce("", combine: { (word1: String, word2: String) -> String in return word1 + " " + word2 })
More%Compactly:
let translation = borks.reduce("", combine: { $0 + " " + $1 })
44
SLIDE 45 Func%on'Composi%on
let translation = split(tweet.body, { (c: Character) in c == " " }) .filter({ (word: String) in countElements(word) > 7 }) .map({ word in "Børk" }) .reduce("", combine: { $0 + " " + $1 }) let translatedTweet = Tweet(username: tweet.username, body: translation) countElements(translation) // == 125!
45
SLIDE 46 Børk
Børk%Børk
Børk%Børk%Børk
Børk%Børk%Børk%Børk%Børk
Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk%Børk
46
SLIDE 47 So#What?
We're%not%doing%anything%here%we%couldn't%do%in% another%other%modern%language
47
SLIDE 48 // split the tweet into words NSMutableArray *words = [NSMutableArray array]; NSMutableString *currentWord = [NSMutableString string]; for (int i = 0, ii = tweet.body.length; i < ii; i++) { unichar currentChar = [tweet.body characterAtIndex:i]; if (currentChar == ' ') { [words addObject:currentWord]; currentWord = [NSMutableString string]; } else { [currentWord appendFormat:@"%c", currentChar]; } } // filter out the shorter words for (int j = words.count - 1; j >= 0; j--) { NSString *currentWord = words[j]; if (currentWord.length < 8) { [words removeObjectAtIndex:j]; } } // map each element of the words array to Børk for (int k = 0, kk = words.count; k < kk; k++) { words[k] = @"Børk"; } // reduce the words back into a new tweet NSMutableString *translatedTweet = [NSMutableString string]; for (int l = 0, ll = words.count; l < ll; l++) { [translatedTweet appendString:words[l]]; } 48
SLIDE 49 // split the tweet into words NSMutableArray *words = [[tweet.body componentsSeparatedByString:@" "] mutableCopy]; // filter out the shorter words [words filterUsingPredicate:[NSPredicate predicateWithBlock: ^BOOL(id evaluatedObject, NSDictionary *bindings) { return [evaluatedObject length] > 7; }]]; // map each element of the words array to Børk for (int k = 0, kk = words.count; k < kk; k++) { words[k] = @"Børk"; } // reduce the words back into a new tweet NSString *translatedTweet = [words componentsJoinedByString:@" "];
49
SLIDE 50 let translation = split(tweet.body, { (char: Character) in char == " " }) .filter({ (word: String) in countElements(word) > 7 }) .map({ word in "Børk" }) .reduce("", combine: { $0 + " " + $1 })
50
SLIDE 51
Write
Less
Code
51
SLIDE 52 Write&Less&Code
<"===">
52
SLIDE 53 Write&Less&Code
<"===">
(less%is%more)
53
SLIDE 54 FP#Checklist
Goal:&Reduce&Complexity
- 1. Stop'branching'(use'closures)
- 2. Stop'looping'(use'closures)
- 3. Reduce'state'(use'immutability)
54
SLIDE 55 Swi$'s'Dark'Underbelly
- 1. It's'new,'and'somewhat'buggy
- 2. Cumbersome'bridging
- 3. No'more'dynamic'dispatch
- 4. Duck@typing'is'no'longer'fun
- 5. No'error@handling
55
SLIDE 56
??
56
SLIDE 57 Thank&You!
Please&evaluate&this&talk&with&the& GOTO&Mobile&App
Marc%Prud'hommeaux%/%marc@impathic.com
but$you$can't$follow$me$on$Twi2er
57