what swift can teach us all

103
What Swift can teach us all! or… How Swift has opened our minds

Upload: pablo-villar

Post on 11-Feb-2017

327 views

Category:

Software


0 download

TRANSCRIPT

What Swift can teach us all!or…

How Swift has opened our minds

What Swift can teach us all!or…

How Swift has opened our minds

Let’s talk about functions!

Let’s talk about functions!*pun intended

fun

Let’s talk about functions!

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

var number = 4 // A boring Int

Let’s talk about functions!

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

var number = 4 // A boring Int

Let’s talk about functions!

var sum = { (a: Float, b: Float) in return a + b }

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

var number = 4 // A boring Int

Let’s talk about functions!

var sum = { (a: Float, b: Float) in return a + b }

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

// A fun...ction

var number = 4 // A boring Int

Let’s talk about functions!

var sum = { (a: Float, b: Float) in return a + b }

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

// A fun...ction

var number = 4 // A boring Int

Let’s talk about functions!

var sum = { (a: Float, b: Float) in return a + b }

let four = sum(2, 2) print(four) // Prints 4.0

Did you know that in Swift you can assign functions to variables?No, seriously…

*pun intendedfun

// A fun...ction

Why the hell would I need that…?

Higher order functions

Higher order functions

Moment…

Higher order functions

Moment…

What’s that?

Higher order functions

Moment…

What’s that?

Roughly speaking…

"A higher order function is a function that accepts functions as parameters"

- Some guy.

let sum = { (a: Float, b: Float) in return a + b } let subtract = { (a: Float, b: Float) in return a - b } let multiply = { (a: Float, b: Float) in return a * b } let divide = { (a: Float, b: Float) in return a / b } func main() { let eight = operateOn(6, and: 2, usingFunction: sum) let four = operateOn(6, and: 2, usingFunction: subtract) let twelve = operateOn(6, and: 2, usingFunction: multiply) let three = operateOn(6, and: 2, usingFunction: divide) } func operateOn(a: Float, and b: Float, usingFunction f: (Float, Float) -> Float) -> Float { return f(a, b) }

let sum = { (a: Float, b: Float) in return a + b } let subtract = { (a: Float, b: Float) in return a - b } let multiply = { (a: Float, b: Float) in return a * b } let divide = { (a: Float, b: Float) in return a / b } func main() { let eight = operateOn(6, and: 2, usingFunction: sum) let four = operateOn(6, and: 2, usingFunction: subtract) let twelve = operateOn(6, and: 2, usingFunction: multiply) let three = operateOn(6, and: 2, usingFunction: divide) } func operateOn(a: Float, and b: Float, usingFunction f: (Float, Float) -> Float) -> Float { return f(a, b) }

We have defined 4 functions

as variables.

let sum = { (a: Float, b: Float) in return a + b } let subtract = { (a: Float, b: Float) in return a - b } let multiply = { (a: Float, b: Float) in return a * b } let divide = { (a: Float, b: Float) in return a / b } func main() { let eight = operateOn(6, and: 2, usingFunction: sum) let four = operateOn(6, and: 2, usingFunction: subtract) let twelve = operateOn(6, and: 2, usingFunction: multiply) let three = operateOn(6, and: 2, usingFunction: divide) } func operateOn(a: Float, and b: Float, usingFunction f: (Float, Float) -> Float) -> Float { return f(a, b) }

We have defined 4 functions

as variables.We pass in those functions in the operateOn call "as if" they were variables… (well, they

actually are)

operateOn is a higher order function, because it accepts a function as an input parameter.

let sum = { (a: Float, b: Float) in return a + b } let subtract = { (a: Float, b: Float) in return a - b } let multiply = { (a: Float, b: Float) in return a * b } let divide = { (a: Float, b: Float) in return a / b } func main() { let eight = operateOn(6, and: 2, usingFunction: sum) let four = operateOn(6, and: 2, usingFunction: subtract) let twelve = operateOn(6, and: 2, usingFunction: multiply) let three = operateOn(6, and: 2, usingFunction: divide) } func operateOn(a: Float, and b: Float, usingFunction f: (Float, Float) -> Float) -> Float { return f(a, b) }

We have defined 4 functions

as variables.We pass in those functions in the operateOn call "as if" they were variables… (well, they

actually are)

Still… Why the hell would I need that???

Let’s see a more practical example…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Let’s see a more practical example…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Mutability

Let’s see a more practical example…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Mutability

Danger!

Let’s see a more practical example…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Mutability

Danger!

But… Hold on… Why?

Let’s see a more practical example…

Nope,it’s not this…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Mutability

Danger!

But… Hold on… Why?

Let’s see a more practical example…

Mutability:

By using mutable variables, we are not 100% sure what the value of the variable at some point is.

It could be modified somewhere, and that leads to the fact that we need to keep track of where that variable is being used and how it’s being modified.

In smaller cases like this, it’s easy to keep track, but…

for jsonRow in rows { var post = Post(json: jsonRow) let postId = jsonRow["id"].stringValue if let storedPost = realm.objects(Post).filter("id = %@", postId).first {

post = storedPost post!.refresh(jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = PostType(rawValue:(storedPost.postType.rawValue | feedType.rawValue)) updates.append(post!.id)

} else { post = Post(json: jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = feedType additions.append(postId)

} realm.add(post!, update: true)

}

How about this…?

for jsonRow in rows { var post = Post(json: jsonRow) let postId = jsonRow["id"].stringValue if let storedPost = realm.objects(Post).filter("id = %@", postId).first {

post = storedPost post!.refresh(jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = PostType(rawValue:(storedPost.postType.rawValue | feedType.rawValue)) updates.append(post!.id)

} else { post = Post(json: jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = feedType additions.append(postId)

} realm.add(post!, update: true)

}

How about this…?

Let’s keep track of post…

for jsonRow in rows { var post = Post(json: jsonRow) let postId = jsonRow["id"].stringValue if let storedPost = realm.objects(Post).filter("id = %@", postId).first {

post = storedPost post!.refresh(jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = PostType(rawValue:(storedPost.postType.rawValue | feedType.rawValue)) updates.append(post!.id)

} else { post = Post(json: jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = feedType additions.append(postId)

} realm.add(post!, update: true)

}

How about this…?

Let’s keep track of post…

for jsonRow in rows { var post = Post(json: jsonRow) let postId = jsonRow["id"].stringValue if let storedPost = realm.objects(Post).filter("id = %@", postId).first {

post = storedPost post!.refresh(jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = PostType(rawValue:(storedPost.postType.rawValue | feedType.rawValue)) updates.append(post!.id)

} else { post = Post(json: jsonRow, endpoint: self.basePath as! ServerEndpoint) post!.postType = feedType additions.append(postId)

} realm.add(post!, update: true)

}

How about this…?

At this point, we can’t easily ensure what the post is going to be…

Let’s keep track of post…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } } return teenagers }

Back to our example…

Could we replace it, somehow, such that we avoid mutability?

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let :)

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let 😊

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let 😊

Yes, emojis are allowed in Swift source code… ✌

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let 😊

Yes, we are tending to become hippies

Yes, emojis are allowed in Swift source code… ✌

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let 😊

Yes, we are tending to become hippies

Yes, emojis are allowed in Swift source code… ✌

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter({ (person) -> Bool in return person.age > 12 && person.age < 20 }) return teenagers }

Higher order functions to the rescue!

// let 😊

Yes, we are tending to become hippies

Yes, emojis are allowed in Swift source code… ✌

If we could just simplify this…

func teenagersFromPeople(people: [Person]) -> [Person] { let teenagers: [Person] teenagers = people.filter { $0.age > 12 && $0.age < 20 } return teenagers }

Simplicity, oh, yeah…

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

Even simpler, baby…

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } }

return teenagers }

Notpreferred

Higher order functions

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } }

return teenagers }

Notpreferred

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

Preferred

Higher order functions

func teenagersFromPeople(people: [Person]) -> [Person] { var teenagers = [Person]() for person in people { if person.age > 12 && person.age < 20 { teenagers.append(person) } }

return teenagers }

Notpreferred

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

Preferred

FILTER

Higher order functions

func idsFromPeople(people: [Person]) -> [String] { var ids = [String]() for person in people { ids.append(person.id) } return ids }

Notpreferred

Higher order functions

func idsFromPeople(people: [Person]) -> [String] { var ids = [String]() for person in people { ids.append(person.id) } return ids }

func idsFromPeople(people: [Person]) -> [String] { return people.map { $0.id } }

Preferred

Notpreferred

Higher order functions

func idsFromPeople(people: [Person]) -> [String] { var ids = [String]() for person in people { ids.append(person.id) } return ids }

func idsFromPeople(people: [Person]) -> [String] { return people.map { $0.id } }

Preferred

Notpreferred

MAP

Higher order functions

func childrenCountFromPeople(people: [Person]) -> Int { var count = 0 for person in people { count += person.children.count } return count}

Notpreferred

Higher order functions

func childrenCountFromPeople(people: [Person]) -> Int { var count = 0 for person in people { count += person.children.count } return count}

func childrenCountFromPeople(people: [Person]) -> Int { return people.reduce(0) { $0 + $1.children.count } }

Preferred

Notpreferred

Higher order functions

func childrenCountFromPeople(people: [Person]) -> Int { var count = 0 for person in people { count += person.children.count } return count}

func childrenCountFromPeople(people: [Person]) -> Int { return people.reduce(0) { $0 + $1.children.count } }

Preferred

Notpreferred

REDUCE

Higher order functions

FILTER

MAP

REDUCE

You want some of the original elements

You want new elements based on the original elements

You want a result based on the original elements

Pure functions

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side

effects.

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side

effects.

• There is a return value

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side

effects.

• There is a return value

• Result depends only on input values

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side

effects.

• There is a return value

• No side effects

• Result depends only on input values

Pure functions

A pure function is a function where the return value is only determined by its input values, without observable side

effects.

• There is a return value

• No side effects

• Result depends only on input values 100% unit-testable!

• There is a return value

• No side effects

• Result depends only on input values

func configureLabelTextWithNumber(number: Int) { self.numberLabel.text = "\(number)" }

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func configureLabelTextWithNumber(number: Int) { self.numberLabel.text = "\(number)" }

• There is a return value ❌

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func configureLabelTextWithNumber(number: Int) { self.numberLabel.text = "\(number)" }

• There is a return value ❌

• Result depends only on input values ✅

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func configureLabelTextWithNumber(number: Int) { self.numberLabel.text = "\(number)" }

• There is a return value ❌

• No side effects ❌

• Result depends only on input values ✅

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func configureLabelTextWithNumber(number: Int) { self.numberLabel.text = "\(number)" }

• There is a return value ❌

• No side effects ❌

• Result depends only on input values ✅

Example #1

Functions using "self.something" are a call to side effects

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• Result depends only on input values ✅

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ✅

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ✅

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

Pure function

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ✅

func labelTextForNumber(number: Int) -> String { return "\(number)" }

Example #1

self.numberLabel.text = self.labelTextForNumber(1)

Pure function

Pure functions

func teenagers() -> [Person] { return self.people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

Example #2

Pure functions

func teenagers() -> [Person] { return self.people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

Example #2

Pure functions

func teenagers() -> [Person] { return self.people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

Example #2

Pure functions

func teenagers() -> [Person] { return self.people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

Example #2

• Result depends only on input values ❌

Pure functions

func teenagers() -> [Person] { return self.people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

Example #2

• Result depends only on input values ❌

How would you test it?

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

Example #2

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

Example #2

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

Example #2

• Result depends only on input values ✅

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

Example #2

• Result depends only on input values ✅

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

Example #2

• Result depends only on input values ✅

Pure function

Pure functions

func teenagersFromPeople(people: [Person]) -> [Person] { return people.filter { $0.age > 12 && $0.age < 20 } }

Example #2

func test15YearsOldIsTeenager() { let filter = PeopleFilter() // System Under Test let someone = Person(name: "asd", age: 15) let input = [someone] let output = filter.teenagersFromPeople(input) XCTAssertEqual(output.count, 1) if output.count == 1 { XCTAssertEqual(output.first!, someone) } }

Pure function

100% unit-testable!

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ❌

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ❌

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Functions involving randomness can never be pure functions

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ❌

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Functions involving randomness can never be pure functionsThey are not unit-testable, because we can't know what expected result is

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ❌

func randomCharacterFromString(string: String) -> String { let maxNumber = UInt32((string as NSString).length) let randomNumber = Int(arc4random_uniform(maxNumber)) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3

Functions involving randomness can never be pure functionsThey are not unit-testable, because we can't know what expected result is , unless…

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

func randomCharacterFromString(string: String, seed: Int) -> String { let maxNumber = Int32((string as NSString).length) srand(UInt32(seed)) // Configure the randomizer's seed let randomNumber = Int(rand() % maxNumber) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3 , unless…

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

func randomCharacterFromString(string: String, seed: Int) -> String { let maxNumber = Int32((string as NSString).length) srand(UInt32(seed)) // Configure the randomizer's seed let randomNumber = Int(rand() % maxNumber) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3 , unless…

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• Result depends only on input values ✅

func randomCharacterFromString(string: String, seed: Int) -> String { let maxNumber = Int32((string as NSString).length) srand(UInt32(seed)) // Configure the randomizer's seed let randomNumber = Int(rand() % maxNumber) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3 , unless…

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ✅

func randomCharacterFromString(string: String, seed: Int) -> String { let maxNumber = Int32((string as NSString).length) srand(UInt32(seed)) // Configure the randomizer's seed let randomNumber = Int(rand() % maxNumber) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3 , unless…

Pure functions

• There is a return value

• No side effects

• Result depends only on input values

• There is a return value ✅

• No side effects ✅

• Result depends only on input values ✅

func randomCharacterFromString(string: String, seed: Int) -> String { let maxNumber = Int32((string as NSString).length) srand(UInt32(seed)) // Configure the randomizer's seed let randomNumber = Int(rand() % maxNumber) let range = NSRange(location: randomNumber, length: 1) return (string as NSString).substringWithRange(range) }

Example #3 , unless…

Pure function

Pure functions

Pure functions

Higher order functions

Protocol-orientedprogramming

Immutability

Genericprogramming

Pattern matchingMVVM

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

But also…

The intention of your code is clearer

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

But also…

The intention of your code is clearer

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

But also…

Code is easier to understand for others

The intention of your code is clearer

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

But also…

Code is easier to understand for others

Less headaches when maintaining each others’ code

The intention of your code is clearer

If you start applying these little yet valuable concepts…

Your brain starts to think in different ways and your mind gets open to learn more and more!

But also…

Code is easier to understand for others

Less headaches when maintaining each others’ code

More effectiveness, more productivity