Instructor: 00:01 At the moment, we're using the single validator function isEmail, to validate the email field, and isNotEmpty to validate the message. As a result, if I go to the form and click submit, you will see that here, we have an email validation message. Empty string, of course, is not a valid email.
00:21 What if I wanted to change this behavior? I want to say that if the user doesn't enter anything, then the message should also be, "This field is required." Only if he has entered an invalid email, then it should have an email validation message.
00:36 Somehow, I would like to say that the email field should be validated using isNotEmpty and isEmail. Of course, this won't compile, because and is for Booleans, but that's the basic idea.
00:53 Let me just open the Elm REPL for a moment. Let's look at this operator with a double arrow, and let's look at the signature. I'm just going to paste it into my editor. You can see that this function with a double arrow takes a function F from A to B, a function G from B to C, and returns a function from A to C.
01:26 This, you could view like this. Essentially, what this does is when you get an A, take that A and feed it to the F function. This will produce a value of type B, and then feed that to the G function to return a C. This is basically function composition. Another way to write this would be G of F(A).
01:50 What I want to do is something very similar, but instead of a function from A to B, I want a validator from A to B, and then a validator from B to C. I want to return a validator from A to C. I don't need these parentheses.
02:17 You can see how the idea is very much the same, because a validator is not exactly a function from A to B, but a function from A to a resolved of string of B. Just so that I don't overload the symbol with a double arrow, let me add an equal in the middle. This will be my function.
02:36 How could we go about implementing this? When I get a value A, I will pass that to the first validator F, and that will return a result. I can then switch in that result. If it's an OK, containing a value of type B, then I can pass that B into my second validator, G. If it's an error with a string, then I will naturally return an error with S.
03:02 This will apply the first validator, and then only apply the second validator if the first one has succeeded. It turns out that some logic like this already exists in the result package. I could simplify this by saying feed A to the first validator, which will yield a result, and then feed that into result and then function, which will take G as its argument.
03:30 Since we've seen the composition operator earlier, we could write this even more tersely without naming A, and using composition here. This notifies the function that when it will get a value, will feed it to F, then it will feed the result of that to result, and then G.
03:53 A sample usage of this would be as follows. We have this validator from string to int., and then we could have a validator isPositive. This would be a validator from int. to int. Implementation is trivial. If I is greater or equal, then zero. Then OK I else error with a string.
04:28 Let's say that I wanted to define a validator, isNatural, that takes a string and returns an int. that should be a positive number, then I could define this like this. I could say that isNatural is isInt., and then compose that with isPositive.
04:50 The way I would use this in my code would be to put this here to say that when I validate an email, I want to first check that it's not empty, and only if that check passes, I want to go on and check if it's a valid email.
05:03 Let me save this. I have a little typo here. Let's see what happens. If I click submit, then I get, "This field is required." If I say, "Hello," and submit, then I get, "Please enter a valid email address."