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") }
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))
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 }
Flatmap
And as monads:
to return an array of non-optional values from an array of oprtional types
let optionalStrings: [String?] = [, nil, , , nil] let strings = optionalStrings.flatMap { $0 } // [, , ]
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 + "!" }
but
let name: String? = nil let greeting = name.map { "Hello " + $0 + "!" }
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) }
but with the string 'forty-two', Int cannot initialize itselfInt 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) }
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.