Functional thinking. Part 6

3r33630. We continue our series of articles on functional F # programming. Today we will talk about the associativity and composition of functions, as well as compare the composition and the pipeline. Look under the cat!
3r33535.  3r33655. 3r33630. Functional thinking. Part 6 3r33535.  3r33655. 3r3599.  3r33655. 3r3618.
The first part is 3r3647.
 3r33655. 3r3618.
The second part is 3r3647.
 3r33655. 3r3618.
Third part 3r3647.
 3r33655. 3r3618.
3r3338. Fourth part 3r3647.
 3r33655. 3r3618.
The fifth part is 3r3647.

3r33535.  3r33655. 3r33548. Associativity and composition of functions 3r33549. 3r33535.  3r33655.

Associativity of functions

3r33535.  3r33655. 3r33630. Suppose there is a chain of functions written in a row. In what order will they be combined?
3r33535.  3r33655. 3r33630. For example, what does this feature mean?
3r33535.  3r33655. 3r33535. 3r33535. let F x y z = x y z 3r33535. 3r33535.  3r33655. 3r33630. Does this mean that function y should be applied to the argument z and then the result should be passed to x ? Ie: 3r3648. 3r33535.  3r33655. 3r33535. 3r33535. let F x y z = x (y z) 3r33535. 3r33535.  3r33655. 3r33630. Or function 3r3602. x applies to the argument 3r3602. y after which the function obtained as a result will be calculated with the argument z ? Ie: 3r3648. 3r33535.  3r33655. 3r33535. 3r33535. let F x y z = (x y) z 3r33535. 3r33535.  3r33655.
  1.  3r33655. 3r3618. True to the second option.  3r33655. 3r3618. The use of functions has 3r33645. left associativity 3r3r6646. .  3r33655. 3r3618. 3r3602. x y z It means the same as 3r3602. (x y) z .  3r33655. 3r3618. A
w x y z equals ((w x) y) z .  3r33655. 3r3618. This should not look amazing.  3r33655. 3r3618. We have already seen how partial application works.  3r33655. 3r3618. If you talk about 3r3r2602. x as a function with two parameters, then (x y) z - This is the result of the partial application of the first parameter, followed by the argument 3r3602. z to intermediate function.  3r33655. 3r3148. 3r33535.  3r33655. 3r33630. If you need the right associativity, you can use brackets or pipe. The following three entries are equivalent: 3r33535.  3r33655. 3r33535. 3r33535. let F x y z = x (y z) 3r33655. let F x y z = y z |> x //use the
direct pipeline. let F x y z = x <| y z
//использование обратного конвейера 3r33535. 3r33535.  3r33655. 3r33630. As an exercise, try to display the signatures of these functions without real computation. 3r33535.  3r33655.

Composition of functions

3r33535.  3r33655. 3r33630. We have mentioned the composition of functions several times, but what does this term really mean? It seems daunting at first glance, but in reality everything is quite simple. 3r33535.  3r33655. 3r33630. Let's say we have a function "f" that matches the type "T1" to the type "T2". We also have a function "g" that converts the type "T2" to the type "T3". Then we can connect the output "f" and the input "g" by creating a new function that converts the type "T1" to the type "T3". 3r33535.  3r33655. 3r33630. 3r33180. 3r33535.  3r33655. 3r33630. For example: 3r3648. 3r33535.  3r33655. 3r33535. 3r33535. let f (x: int) = float x * 3.0 //f is a function of type int-> float
let g (x: float) = x> 4.0 //g is a function of type float-> bool 3r33535. 3r33535.  3r33655. 3r33630. We can create a new function "h", which takes the output "f" and uses it as input for "g". 3r33535.  3r33655. 3r33535. 3r33535. let h (x: int) =
let y = f (x)
g (y) //return the result of calling g 3r33535. 3r33535.  3r33655. 3r33630. Slightly more compact: 3r33535.  3r33655. 3r33535. 3r33535. let h (x: int) = g (f (x)) //h is a function of type int-> bool
3r33655. //test 3r33655. h 1
h 2 3r33535. 3r33535.  3r33655. 3r33630. 3r3645. So far, so simple. This is interesting, we can define a new function "compose", which takes the functions "f" and "g" and combines them without even knowing their signatures. 3r33535.  3r33655. 3r33535. 3r33535. let compose f g x = g (f (x)) 3r33535. 3r33535.  3r33655. 3r33630. After execution, you can see that the compiler correctly decided that " F " Is a function of a generalized type, 'a to generalized type 3r3602. 'b , and " g " is limited to input of type 3r3602. 'b : 3r33535.  3r33655. 3r33535. 3r33535. val compose: ('a ->' b) -> ('b ->' c) -> 'a ->' c 3r33535. 3r33535.  3r33655. 3r33630. (Note that generalized composition of operations is possible only because each function has exactly one input parameter and one conclusion. This approach is impossible in non-functional languages.) 3r3648. 3r33535.  3r33655. 3r33630. As we see, this definition is used for the operator "3r3602. ". 3r33535.  3r33655. 3r33535. 3r33535. let () f g x = g (f (x)) 3r33535. 3r33535.  3r33655. 3r33630. Thanks to this definition, it is possible to build new functions based on existing ones with the help of composition. 3r33535.  3r33655. 3r33535. 3r33535. let add1 x = x + 1
let times2 x = x * 2
let add1Times2 x = () add1 times2 x
3r33655. //test 3r33655. add1Times???r3r3603. 3r33535. 3r33535.  3r33655. 3r33630. Explicit recording is quite cumbersome. But you can make its use easier to understand. 3r33535.  3r33655. 3r33630. First, you can get rid of parameter 3r3602. x , and the composition will return partial application. 3r33535.  3r33655. 3r33535. 3r33535. let add1Times2 = () add1 times2 3r33535. 3r33535.  3r33655. 3r33630. Secondly, because 3r3602. is a binary operator, you can put it in the center. 3r33535.  3r33655. 3r33535. 3r33535. let add1Times2 = add1 times2 3r33535. 3r33535.  3r33655. 3r33630. The use of the composition makes the code cleaner and clearer. 3r33535.  3r33655. 3r33535. 3r33535. let add1 x = x + 1
let times2 x = x * 2
3r33655. //as of the old 3r33655. let add1Times2 x = times2 (add1 x)
3r33655. //new
let add1Times2 = add1 times2 3r33535. 3r33535.  3r33655.

Using the composition operator in practice 3r3454. 3r33535.  3r33655. 3r33630. The composition operator (like all infix operators) has a lower priority than normal functions. This means that the functions used in the composition can have arguments without using parentheses. 3r33535.  3r33655. 3r33630. For example, if the functions "add" and "times" have parameters, they can be transferred during composition. 3r33535.  3r33655. 3r33535. 3r33535. let add n x = x + n
let times n x = x * n
let add1Times2 = add 1 times 2
let add5Times3 = add 5 times 3
3r33655. //test 3r33655. add5Times???r3r3603. 3r33535. 3r33535.  3r33655. 3r33630. As long as the corresponding inputs and outputs of the functions match, the functions can use any values. For example, consider the following code that performs a function twice: 3r33535.  3r33655. 3r33535. 3r33535. let twice f = f f //signature ('a ->' a) -> ('a ->' a) 3r33535. 3r33535.  3r33655. 3r33630. Notice that the compiler infers that “ F ” Accepts and returns values ​​of the same type. 3r33535.  3r33655. 3r33630. Now consider the function " + ". As we saw earlier, input is int th, but the conclusion is really - 3r3602. (int-> int) . Thus, “ + ” Can be used in “ Twice ”. Therefore, you can write: 3r33535.  3r33655. 3r33535. 3r33535. let add1 = (+) 1 //signature (int -> int) 3r3655. let add1Twice = twice add1 //signature as well (int -> int) 3r3655. 3r33655. //test 3r33655. add1Twice 9 3r33535. 3r33535.  3r33655. 3r33630. On the other hand, you cannot write: 3r33535.  3r33655. 3r33535. 3r33535. let addThenMultiply = (+) (*) 3r33535. 3r33535.  3r33655. 3r33630. Because the input "*" should be int , not 3r3602. int-> int 3r3603. function (which is the output of the addition). 3r33535.  3r33655. 3r33630. But if to correct the first function so that it returns only 3r3602. int , everything will work: 3r33535.  3r33655. 3r33535. 3r33535. let add1ThenMultiply = (+) 1 (*) 3r3655. //(+) 1 with the signature (int -> int) and the result of 'int'
3r33655. //test 3r33655. add1ThenMultiply ???r3r3603. 3r33535. 3r33535.  3r33655. 3r33630. The composition can also be performed in reverse order by means of “3r3602. ”, If necessary: ​​ 3r33535.  3r33655. 3r33535. 3r33535. let times2Add1 = add 1 times 2
times2Add???r3r3603. 3r33535. 3r33535.  3r33655. 3r33630. The reverse composition is mainly used to make the code more similar to English ("English-like"). For example: 3r3648. 3r33535.  3r33655. 3r33535. 3r33535. let myList =[]3r33655. myList |> List.isEmpty |> not //direct conveyor 3r3655. 3r33655. myList |> (not List.isEmpty) //use inverse composition 3r3603. 3r33535. 3r33535.  3r33655.

Composition vs. conveyor 3r3454. 3r33535.  3r33655. 3r33630. You may be confused by the small difference between the composition and the conveyor, as they may look very similar. 3r33535.  3r33655. 3r33630. First, look at the definition of a pipeline: 3r33535.  3r33655. 3r33535. 3r33535. let (|>) x f = f x 3r33535. 3r33535.  3r33655. 3r33630. All this allows you to put the arguments of functions before it, and not after. That's all. If the function has several parameters, then the input must be the last parameter (in the current parameter set, and not in general). Example seen earlier: 3r33535.  3r33655. 3r33535. 3r33535. let doSomething x y z = x + y + z
doSomething ??? //all parameters are listed after the function 3r3655. 3 |> doSomething 1 2 //The last parameter is pipelined to function 3r3603. 3r33535. 3r33535.  3r33655. 3r33630. The composition is not the same and can not be a substitute for the pipe. In the following example, even the number 3 is not a function, so the "output" cannot be transferred to 3r3602. doSomething : 3r33535.  3r33655. 3r33535. 3r33535. 3 doSomething 1 2 //error
//f g is the same as g (f (x)) so we can rewrite this: 3r3655. doSomething 1 2 (3 (x)) //it is assumed that 3 must be a function! 3r33655. //error FS0001: This expression was expected to have type 'a ->' b 3rr3655. //but here has type int 3r33535. 3r33535.  3r33655. 3r33630. The compiler complains that the value "3" should be a type of function 3r3602. 'a ->' b . 3r33535.  3r33655. 3r33630. Compare this with the definition of a composition that takes 3 arguments, where the first two should be functions. 3r33535.  3r33655. 3r33535. 3r33535. let () f g x = g (f (x)) 3r3655. 3r33655. let add n x = x + n
let times n x = x * n
let add1Times2 = add 1 times 2 3r33535. 3r33535.  3r33655. 3r33630. Attempts to use the pipeline instead of composition will result in a compilation error. In the following example, “ Add 1 ” Is the (partial) function int-> int 3r3603. which cannot be used as the second parameter for " times 2 ". 3r33535.  3r33655. 3r33535. 3r33535. let add1Times2 = add 1 |> times 2 //error 3r3655. //x |> f is the same as f (x) so we can rewrite this: 3r3655. let add1Times2 = times 2 (add 1) //add1 should be 'int'
//error FS0001: Type mismatch. 'int -> int' does not match 'int'
3r33535. 3r3-3635.  3r33655. 3r33630. The compiler will complain that " Times 2 " It is necessary to take the parameter 3r3602. int-> int 3r3603. i.e. be a function of 3r3602. (int-> int) -> 'a . 3r33535.  3r33655. 3r33548. Additional resources 3r3549. 3r33535.  3r33655. 3r33630. For F #, there are many tutorials, including materials for those who come with C # or Java experience. The following links may be helpful as you learn more about F #: 3r33535.  3r33655. 3r3599.  3r33655. 3r3618. 3r3-3559. F # Guide  3r33655. 3r3618. 3r33535. F # for Fun and Profit  3r33655. 3r3618. 3r? 3569. F # Wiki  3r33655. 3r3618. 3r? 3574. Learn X in Y Minutes: F #  3r33655. 3r33535.  3r33655. 3r33630. Also described are some more ways like 3r38282. start exploring f # . 3r33535.  3r33655. 3r33630. Finally, the F # community is very friendly to beginners. There is a very active chat in Slack, supported by the F # Software Foundation, with rooms for beginners to which you are Feel free to join . We strongly recommend that you do this! 3r33535.  3r33655. 3r33630. Do not forget to visit the site 3r3638. Russian-speaking community F # ! If you have any questions about learning the language, we will be happy to discuss them in chat rooms: 3r33535.  3r33655. 3r3599.  3r33655. 3r3618. room #ru_general in 3r3619. Slack chat F # Software Foundation
 3r33655. 3r3618. 3r3609. chat in Telegram
 3r33655. 3r3618. 3r31414. chat in gitter
 3r33655. 3r3618. Room #ru_general in Slack chat F # Software Foundation
3r33535.  3r33655.

About the authors of the translation

3r33535.  3r33655. 3r33630. Author translation 3r3631. 3r3645. @kleidemos
3r33535.  3r33655. Translation and editorial edits made by the Russian-speaking community for F # developers . We also thank r3r3640. 3r3645. @schvepsss
and 3r3644. 3r3645. @shwars
for the preparation of this article for publication.
3r3656. 3r33655. 3r33655. 3r33655.
! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d,! 1): e.attachEvent ("onload", d ): d ()}}} t ("//"""_mediator") () (); 3r3654. 3r33655. 3r3656.
+ 0 -

Add comment