📜 ⬆️ ⬇️

A bit about functors and higher order functions in Swift

Collections


Developers who switched to Swift from Objective-C could not help but notice the most convenient functionality that Swift provides for working with collections. Using ranges in indexes
let slice = array[1..<10] 
convenient syntax for initializing and adding an item to the collection, extensibility, and of course higher order functions

Filter


The most commonly used function for collections is probably filter
 let alex = Person(name: "Alex", age: 23) let jenny = Person(name: "Jenny", age: 20) let jason = Person(name: "Jason", age: 35) let persons = [alex, jenny, jason] let jNamedPersons = persons.filter { $0.name.hasPrefix("J") } // [jenny, jason] 


Reduce


Less commonly used, but extremely expressive and convenient is the function reduce.
 let ages = persons.map{ Float($0.age) } let average = ages.reduce(0, +) / Float(persons.count) 

')
You can write your own higher-order functions, and this is quite fascinating:
 func divisible(by numbers: Int...) -> (Int) -> Bool { return { input -> Bool in return numbers.reduce(true) { divisible, number in divisible && input % number == 0 } } } let items = [6, 12, 24, 13] let result = items.filter(divisible(by: 2, 3, 4)) // [12, 24] 


Map


The functional concepts of functors and monads came from Haskell. They say it is impossible to simply take and understand what a monad is, and even more so it is impossible to explain it. However, we can temporarily drop all the difficulties and explain to ourselves only what is really necessary, and those who want to dig deeper can begin by learning Haskell.

So, for simplicity, we can assume that the functor is a container to which the map function is applicable, and a monad is a functor to which the flatMap function is applicable.

Since collections are containers, and in Swift the map function is defined for them, they can act as functors:
in order to transform a collection of one type into a collection of another type, take our persons array and get from it an array of ages like [Int]
 let ages = array.map{ $0.age } // [23, 20, 35] 


Flatmap


And as monads:
to return an array of non-optional values ​​from an array of oprtional types
 let optionalStrings: [String?] = ["a", nil, "b", "c", nil] let strings = optionalStrings.flatMap { $0 } // ["a", "b", "c"] 

in order to expand the original collection
 let odds = [1,3,5,7,9] let evensAndOdds = odds.flatMap { [$0, $0 + 1] } // [1,2,3,4,5,6,7,8,9,10] 



Optionals


But map and flatMap can be applied not only to collections. It is extremely useful to use Optional types as monads:

Map


If Optional has a value, then the result of the map is returned with this value, if there is no value, then nil is returned:
 let name: String? = "World" let greeting = name.map { "Hello " + $0 + "!" } // "Hello World!" 

but
 let name: String? = nil let greeting = name.map { "Hello " + $0 + "!" } // nil 


Flatmap


FlatMap works much the same way, with the only difference that the result of the flatMap can return nil, and map cannot

 let string: String? = "42" let number = string.flatMap { Int($0) } // 42 

but with the string 'forty-two', Int cannot initialize itself
Int can't but swift can much
 let formatter = NumberFormatter() formatter.numberStyle = .spellOut formatter.locale = Locale(identifier: "RU") let a = formatter.number(from: " ") 
// 42

 let string: String? = " " let number: Int? = string.flatMap { Int($0) } // nil 

if we try to use the map
 let number: Int? = string.map { Int($0) } 
see an error.

Conclusion


Using monads and functors when working with Optional variables can significantly reduce the amount of code and make it more intuitive.

Source: https://habr.com/ru/post/322806/


All Articles