object-oriented

This is a piece of code that I wrote earlier. [receiver doThis]; We’ve written a lot of this code. It’s a tool we use to see the world. We view the world from an object-oriented perspective, and then we send messages to the recipient. Sometimes we send messages with parameters. So we’re going to add colons here, sometimes a lot of colons. But we’ve been doing this kind of programming for a long time, because we only have to send messages to the receiver, and that’s what object orientation is all about.

Objective-c and function calls

We use the interesting syntax of square brackets and colons, and all you need to know is that this is provided for us. You know, we got a big surprise, and we did this because that’s how the programming world works, but that’s not how the real world works. There’s nothing magical about this syntax, we know how it works, that’s what Objective-C does.

[receiver do: this
        with: that];
        
objc_msgSend()
Copy the code

Objective-c gives us function calls, these square brackets that we’re talking about, you have to send a message to the receiver, just to help us see the world, but it has nothing to do with the computer itself.

This way of organizing things with object orientation, that’s the way we look at things, so objects have internal properties, states, and you want to avoid directly manipulating the state of objects, because that’s what C programmers do.

The function is not as simple as we think

So this gives us these entry points, and these are the methods that you’re going to call, where you’re going to indirectly change the state. You will send a message to the recipient. Then you find those extension points, and you send messages to the receiver, and that’s how we think about programming. Object oriented.

We think, well, with the Squeak robot kit, we can build robotic cars, and if we want the car to move, we’ll have to send messages to the car, so maybe we’ll have a steering wheel, and the steering wheel will send messages to the car in some way. Now, with Swift, we can do a lot more, and now we can control the robot with an iPad.

We still need to send these messages to objects, which is object-oriented, or in the case of these robots, Deval, because we don’t know how it’s done, it’s probably programmatic.

At the bottom, we’re using the functional part, because we’ve been told it’s cool, and if we don’t read Haskell, or Monad, we can still use square brackets and colons. So, we know a lot about object orientation, so we don’t need to talk about it today, we know a lot about the program part of it.

Function is not as simple as we think, we talked about object orientation earlier, because that’s the way we look at the world, but maybe function is the way we look at the world. So imagine that when we learn something like Swift or Objective-C or Java, we always start with “Hello, world.” It seems to be an unwritten rule.

So in Swift, our “Hello world” is actually very unnatural to people. This function doesn’t get anything, and it doesn’t return anything. We think the concept is very simple and the code is very simple, but conceptually, it is very different from what we think of as a function.

“Hello, Daniel”

func hello(_ name: String) {
    print("Hello, \(name)!")
}
Copy the code

We think that a function needs to receive something, and it needs to give you something back, so there’s a function called side-effect. So we’re going to say, well, once you understand what it does, so we’re going to put an underscore here, because Swift lets us do that, and it makes the call look comfortable, so I can call it like this: Hello (“Daniel”), and then it can print “hello, Daniel”, but that’s still a problem, even though we passed in a string, it didn’t return anything.

This is a very strange function, as you all know, if you think back to high school math. So when we think of a function in mathematics, we think of a function as something that takes a set of elements and maps it to a set of elements in another range. And, if you like flowers, then you might want to be like the guy outside, because it’s going to be more like this.

Last year, I introduced you to a passage THAT I wrote on the blackboard in the orientation program. Basically, it says: “For every element in the element set, there must be a unique element in some range.” This functionality requires fetching something from the set of elements and then providing something in scope.

mapping

If you give a function the same input, it will give you the same output every time. You can’t give a function some value, and it gives you a value, and you give it this value, and it gives you another value. That’s the idea of a function. It maps something to something else. But it can’t do this because the concept of the function is confusing.

So the first thing is, does it start at the top, or does it start in the middle? It doesn’t matter. You can map two different elements, y equals x squared, two different x’s to the same square, and that’s fine. But these x values are not to be confused. Also, your math teacher might have drawn something similar on the board and called it a Function machine.

So HERE I’ve created my own function machine, which is my own function. It takes an input from which it produces the corresponding output. I’m not going to output something to the console, I need to return something, so I’m going to return a string. This is a complete function with input and output, so when I put the string “Daniel” in there, it gives me the corresponding output, which makes me feel a lot better, and it gives me “Hello, Daniel” back, which makes my day.

So this is the function, and this is what we defined it to be. It cannot take nothing and return nothing. In addition, there are no side effects of the function, so this is the function we summarized. In a mathematical sense, a function is a mathematical tool that we learned in high school algebra. It is also a function without side effects.

Testability, repeatability

One of the things that scares us a lot is that when we’re programming, we might mutate or change the state in some place that we don’t notice, so we don’t want to change the state, and if we don’t change the state, it’s testable, and it’s repeatable.

Let’s go back to the car example. We might have a non-functional approach where I need to tell the car to move forward, but there are a lot of problems with that approach. One of the problems is that sometimes I might make the car a global variable, so when I control the car, I tell the car, “Hey, can you change your position? “

And then I go into this vehicle object, and I know that it has a position property in it, but more importantly, I know that position, not because I’m simply incrementing it, but because I’m manipulating it directly, which I think is bad.

This is also a problem, if I repeat this function, then I’ve moved the car a certain distance, and then I’ve moved the car a certain distance again, and I’ve given the same input twice, but I’m getting a different output, because my car is in a different place.

So that’s hard to test, so we think object orientation is all about objects. So I need to create objects, but this is Swift, so a structure is a little bit better than an instance of a class, so I’m going to create a structure, because I read a book somewhere that suggested I do that. So now I have a vehicle object, and then the vehicle has to have a location property, and then the location changes, so it has to be a var.

Hopefully no one sees my code in code review and sees this var, but we go ahead and change this variable so that my function has to be able to change it, which is pretty annoying.

The variable function

struct Car {
    let position: Int
    
    func forward(_ amount: Int) -> Car {
        return Car(position:  position + amount)
    }
}
Copy the code

There’s a variable, and then there’s this mutating function, but I’m still sending a vehicle instance to this method and saying, “Hey, my car has moved forward by this amount! “That’s what we’ve been doing in Objective-C for years, and we feel good about it because otherwise we’d still be using CG scraps.

So I moved the car forward a little bit, but I was still testable. I move the car forward. And then I move forward again, and I get different results both times. So I thought, well, what if I make this variable always on? So INSTEAD of using var, I decided to use let, so instead of using the change function, what if I returned a new vehicle instance? What if I hadn’t moved the car forward? But when I tell the car, “Hey, move forward!” “It replies,” Ok, but now you have to use this car. “

So I created a new vehicle instance in this new location. I thought it was a great code, and THEN I tried to teach it to people, that this is how the programming world really works, that every time you move a car forward, I’ll just give you a new car.

Somehow, this doesn’t fit with my worldview. So instead of giving me a new car, I’m thinking, what would it be like if it was a CarView?

class CarView: UIView {
    private var car: Car
    // some init
    
    override func draw(_ rect: CGRect) {
        // code to draw the car
    }
}
Copy the code

This is a way of representing the car, but it doesn’t represent the car itself, so I’ve now created a class called CarView, and this CarView has a car object in it. The car knows where it is, and all I have to do is draw the car, so I’m going to draw where the car is and what it looks like, and then I’m going to separate these things out, and I feel pretty good about that, because I’ve turned this functional thing into a non-functional thing. If I get rid of the concept of just using functional or object oriented, or how can I put it, I’m getting the feel for it now. We can do this by saying, “Are you really a functional programmer? “

So we end up with this combination, and I know it looks a lot like objects and properties, but I want to show you a whole new way of doing it. I want you to think of it as a mutable CarView on the outside, so that we don’t interact directly with non-mutable functional content on the inside.

It’s a good idea, because it’s testable. I get a vehicle object, and THEN I move it, and then I get a new car in the new location. Then I’ll move the car a different distance again, and then I’ll get a different vehicle object. If I move the same distance, then I’m always going to get these testable, repeatable results, which means I’m going to get variable externals, things that interact with each other, so I can combine these things, and this looks very similar to MVC, right?

Our view controllers talk to each other, but the view controllers hide the communication with the view. In addition, the view controller hides the communication with the model, so it’s just a way to organize our code. The same is true for functional, object-oriented, and sometimes problems require different solutions.

We can wrap this nice shell around this soft thing in the middle and make a pleasant candy. Immutable function, and then change around the core of the function, that’s good news, right? Why does Haskell need Monads? Haskell needed Monads because there was no variability and nothing was ever going to change, so they needed to introduce it so that we could capture external and internal states and then modify things and interact with them. We don’t need this thing, because we’re not a purely functional language.

We don’t need Monads

In Swift, we know these concepts from Haskell and Skala and other functional programming languages through gyration, but we don’t necessarily need them. We have classes, we have structures, we have other things that are different from functional programming, and we can take advantage of those different things, as long as we can keep the separation clear and know when to interact with the outside world and when not to. So we know when we can separate.

But some people say, we can use Monads now, too. The optional values come from Monads. But the point is we don’t have Monads, we don’t need them. They are not required in the Swift system. So Swift supports the functional programming paradigm, which supports this way of thinking about the world, but is not required. Swift is not a purely functional programming language.

We’ve got structs and classes, we’ve got lets and var, we’ve got mutable methods and immutable methods, so the point is, Swift just needs to pick the best. It supports these functional ideas, but we are not constrained by them.

We don’t have to be constrained by language constraints, and of course, we know that in Objective-C, we can expose these things that are supposed to be private by using categories or class extensions and things like that somewhere, so we don’t have to be constrained by them, but the idea is, We should try to separate these things as much as possible. Monads are not required, which means they are optional.

Learn functional programming in Swift

I want to talk a little bit more about functions and how to learn functional programming, especially in the Swift context. When we hear the word mapping, what we hear is that we need mapping because loops are ugly. But loops are actually not ugly.

Another thing people misunderstand is that mapping is not a substitute for loops. We think this is only part of it, because every example we’ve seen is mapping in an array environment. In the case of arrays, we have hidden the loop over arrays in the map.

So there’s a function here. I in the iTunes store, and then given a string, a show that I need to search, then it will return a string, which is the actual search words, so when I passed a: food and cooking string, if you have ever searched for this piece of string in the iTunes store, then it will appear after the space position.

Therefore, there is a transformation from a string to the other characters in a string of function, mapping the idea is, if I am given an array of strings, and then I want to improve (lift) the function, so before the string is a string of transform to another string, enter it now, I can give a set of strings, It can then return another set of strings.

extension Array where Element == String {
    func searchStrings(using f: (Element) -> String) 
                                                  -> [String] {
        var result = [String]()
        for entry in self {
            result.append(f(entry))
        }
        return result
    }
}
Copy the code

So to review the concept of rotation, do you like where Element == String in Swift 3.1? Oh my god, I love this grammar, we’ve been waiting for this for years! So this is only for string arrays, if we have an array of strings, then I can extract these two search strings, which is this function that transforms from one string to another, and promote it to another function, which takes an array of strings and returns an array of strings. So we’ve done what we need to do, which is iterate through the strings of this array, and then map each item, and that’s what mapping does.

That’s my string search method, so I can pass in whatever I want to search for. I can pass it like this. I could use a string to search for another string, perhaps because it is a trailing closure, and THEN I looked up the usage in StackOverflow, so I used $0 here. But in either case, when I map each of these strings individually into an array, I get what I want.

Map an array

When we’re mapping an array, what we’re thinking is, I have A function that maps A to B, and I want to promote it to be able to map an array of A to an array of B. And then the thing that maps an array of A to an array of B is called an F (x) mapping. So in general, I think, the mapping f(x) can change the promotion of A, the array of A, to the promotion of B, the array of B. So if we just repeat it over and over again in various situations, you’ll understand exactly how mapping works.

So now when I map it this way, or when I map it with $0, I can get the same result again, because map is built into the Swift standard library. But again, that doesn’t mean loops are bad, so maps don’t replace loops, so what I want to tell you is that I want to use maps and optional values.

Mapping with nullable values

There is a mapping with nullable values. There’s an array of myFaves. There’s also a kimFaves array, uh, Kim doesn’t have any values in it, it’s an empty array, and what I’m going to do is I’m going to use the danielFaves array to get its first element, but when you look for the first element in any array, you might get an optional value, Because this array probably doesn’t have a first element. You know, because kimFaves doesn’t have any values, but myFaves does. So I’m going to get a nullable.

So if I say, “Get a String from danielsFaves. First,” that won’t do anything, because String needs an element that actually has a String value, but here we’re passing a nullable String, so… If we use if let, then we get the right result because we know packages for nullable strings, but if we use if let Kim, then we get nothing because the value is empty and we don’t unpack it and skip the if block.

So we want to promote this function from one string to another to a nullable string to another nullable string. So I want to pause for a moment and share with you one of my thoughts. Suppose we have a function that maps one type to another, let’s say String -> Int. And then I have a function that maps an Int to a Double.

Visualization tool

So imagine having a visual tool that allows me to display all of these things, and because it produces an Int and takes an Int as a parameter, I can combine them together, and I can visually represent the combination of functions, and I can build it like an integrated circuit, Just let the parameters pass in the correct order. I can open up this thing, which is a function that maps String to Int, so I can put any function like that in there. Now I’ve got a pipeline, matched to a set of ints, and finally, by combining these things together, I’ve got a function that maps a String to a Double, and boy, look what I can do next!

I can do this, well, I don’t want to provide a single string, I want to provide an array of strings. The result should therefore be an array of type Double. So in other words, I’ve encapsulated this combination in a mapping. I don’t want to supply an array of strings, I want to supply a nullable string, and then I should be able to get a nullable float. So I encapsulated this whole combination of functions into a map. That’s what maps really do, they help me transform from one type to another.

Can be null

extension Optional where Wrapped == String {
    func searchString(using f:(Wrapped) -> String) 
                                            -> String? {
        switch self {
        case .none:
            return nil
        case .some(let value):
            return .some(f(value))
        }
    }
}
Copy the code

So now have a mapping, and can be null, as we like to extend an array, we can be a null value for extension and now I let the mapping from a string as the function of another string, here is the the function of the incoming I now will I let it generate a converts to an empty string to another can be a function of the empty string, Just like we did with arrays earlier.

If you don’t remember, I can do it again. In this case, I switch self, and if you’ve ever talked to a functional programmer, they’ll tell you “naturally.” This is too bad. But, let’s say, if I give you a nil value, then you need to return me a nil. There’s nothing to do here. But if I’m giving something that’s not empty, then you need to give me something that’s not empty.

This is a function that maps a string to another grandfather, so if I give you a nullable string, and the contents of that nullable string are nil, then you have to return me nil. If not, perform the mapping. So, you need to take this.some thing and bind it to the value that I gave you, and then you’re going to evaluate that value and encapsulate it so that it becomes a nullable value, so of course it’s a natural thing to do.

That is, if I want to get the first element of danielsFaves, I get a nullable value. Then I call searchString for this nullable value. With this function, I get the “food” and “cooking” elements. I’ll hide the unpacking operations as we explore packages. I’ve hidden the if let. Then I also hide the switch in the same way, so the map doesn’t actually equate to a loop, but rather implies a transition from one state to another. And kimFaves doesn’t have any elements in it, so I try to execute this function on it, good, and it gets.None, so it will return nil to me. So the combination of a map with an optional value is very similar to the combination of a map with an array.

I’m elevating this function from A to B to be A function from nullable A to nullable B, and similarly, to be A function from A mapping to A mapping of B. Okay? So I can do this, and instead of just using the mapping, like we did with the array, when I execute this function, I’m going to be able to get food and cooking, and then I’m going to do the same thing with kimFaves, and I’m going to get nil.

So, anyway, I have A function that converts A to B, and then with this mapping function of f of x, I can convert that to A mapping of A to A mapping of B. As long as you’re confident, people will believe you know what you’re talking about.

Implement your own mapping

If you want to fully understand these concepts, you need to implement the mapping functionality yourself. When you come across a situation like this, you might think, “Oh, is there anything I can do to help me solve this problem? It’s you, Mapping!” So, I’m going to give you a quick example that you can try to do, but it’s worth it, because the Swift library doesn’t provide you with this thing, which is the Result type.


enum Result<Value, Error> {
    case success(Value)
    case failure(Error)
}

Copy the code

In Swift, Result was older than Error, and we still use it a lot. Both parameters of Result are generic. It requires either a Value or Error type and, if successful, returns the resulting Value wrapped in the.success enumeration Value; If it fails, I return the error message wrapped in a.failure enumeration value. This is very similar to nullable values, where.failure corresponds to.None and.success to.some. So, right! If I write a function that maps Result to another Result, THEN I get something that maps ResultA. Error to resultb.error. Both of these are Error, just like they were nil before, but the difference is, I’m going to promote the mapping from A to B to ResultA. Error to ResultB. Error. Let’s extend Result. Since both Value and Error of Result are generic, my function from A to B will become A function from Value to TargetValue, and I will end up with A Result object with TargetValue and Error types.

extension Result {
    func map<TargetValue>(_ f: (Value) -> TargetValue) 
                                -> Result<TargetValue, Error> {
        switch self {
        case .failure(let error):
            return .failure(error)
        case .success(let value):
            return .success(f(value))
        }
    }
}
Copy the code

The next thing I’m going to do is the same thing I did with the optional, I’m going to switch on self, and if it’s.failure, then I’m going to map the Error message just like we mapped nil. If it’s not, then I’m going to bind this value, and I’m going to use this value to evaluate f of x, and that’s what I get. And I’m going to skip through this very quickly, because I want to get this little exercise over with.

The problem with mapping is that people often think of it as a container that contains elements of a certain type. This is what we call an array of type A, nullable A, and A Result that contains values.

This is not a container

I want to take an example that is not a container. There is a mapping that converts a Double to another type. So I created a function that converts from Double to String, a function that converts from Double to URL, and a function that converts from Double to Any. So whatever the purpose of the function is, just use generics. So here we’re going to build a function that converts from Double to some kind of generic type.

Here is an example of what I created. This function takes a product of one to the fourth power to generate an array of doubles. I pass in an X and it returns X, x², x³ and x⁴. That is, it returns an array of four elements. So if we pass 5.2 Double, it returns 5.2 to the first, second, third, and fourth powers, which is what we get. Why do we do this? We don’t actually do that. You just have to follow my lead. Let’s look at another example.

This time I’m just going to print description, so we need to have a function that converts Double to String, which simply returns X. description, So the String representation of 5.2 Double is 5.2 String.

Now let’s write the mapping for this type. The function converts from Double to another type. Since it does not belong to the container, I need to perform the same promotion. So this is what the mapping actually looks like.

I’m going to pass in this function that converts A to B, just like we did in the other mappings. Any other mapping will accept this function that A converts to B. Remember, since I started with A function from Double to A, and I want to end with A function from Double to B. So I need to combine them. So what I’m going to do is I’m going to take this function from Double to A, through f of x, and then I’m going to use the combination to give me the mapping.

So my result is going to be this function from Double to B, and the way I’m going to do it is I’m going to take f of x from Double to B. This code will be clearer if I remove self. Ok, so I need A mapping from A to B, so here we have A mapping from Double to String. We add all the doubles together and print out the value.

Double mapping

So what does a mapping of type Double look like? There’s a function that converts from a Double to an array of doubles. There are also functions that convert to String. So what does a String map look like? There’s a function from Double to String.

So I took this f of x mapping from Double to String, and I elevated it to this horrible thing. Which is this function, which converts a Double to Double array function to a Double to String function. So I’ve got this mapping of A, which is A function from Double to Double, and then this f of x, which takes A Double to A String as an argument. But I was also worried that people would think, “Oh, we heard you speak for 25 minutes and we still don’t know what you’re talking about.”

The point is that what people usually talk about in functional programming is mapping, and once you start writing code like this yourself, mapping isn’t really that mysterious. Another option is to hide this part of the functional core inside mutable functions and give it a flexible external interface. So, that’s why you use functional programming.

Time for questions!

** Q: ** You mentioned that mapping is part of a functional function. So we have other things, like reduce, things like that. Does the same apply? Or do you have another suggestion?

** A: ** Well, in short, if you use a structure like an array, a nullable, something that supports mapping, then essentially we call it a functer. So the mapping is actually a little bit special. Another thing that I didn’t really want to mention, but you brought it up again, is that assuming we have something like flatMap, we’re essentially using Monad, so the scope is much bigger than people think.

For convenience, we also have specifications, filters, and other functional functions. The key to these features is that they don’t modify what you pass in, they just let you pass it in, and then the other end returns it back to you, but what was passed in before hasn’t changed, so there’s a difference between sort and sorted for arrays. If you want to sort an array, do you want to change it? Or do I get an array from the other end?

So the reason I want you to use functions is because you can isolate code that doesn’t change. In his talk, Gary Bernhardt talked about separating out the code that changes the state. So if you do that, you’ll find that your code is much easier to maintain, and our iOS code is very comfortable with that. I know people are always arguing, “It’s not MVC, it’s MVVM!” But it’s not. We simply separate the code that changes the state from the code that doesn’t. We just have different ways of matching them. thank you