Functional thinking. Part 2

Original author: Scott Wlaschin
  • Transfer

Friends, we continue to understand the functional programming. In the second part of this series of articles, you will learn the basic principles of this development paradigm and understand how this approach differs from object-oriented or imperative programming.

Values ​​and Function Values

Consider this simple function again.

let add1 x = x + 1

What does " x" mean here ?

  1. Take some value from the domain.
  2. Use the name " x" to provide this value so that you can refer to it later.

Using a name to represent a value is called “binding”. The name " x" is "bound" to the input value.

So if you calculate a function with an input value of, say, 5, then the following will occur: wherever there is “ x” in the original definition, the value 5 is set, similar to the function “find and replace” in a text editor.

let add1 x = x + 1
add1 5
// заменяем «x» with «5»
// add1 5 = 5 + 1 = 6
// результат  6

It is important to understand that this is not an assignment. “ x” Is not a “slot” and is not a variable with an assigned value that can be changed later. This is a one-time association of the name " x" with a certain value. This value is one of the predefined integers; it cannot be changed. Those. once bound xcannot be changed . A label once associated with a value is permanently associated with that value.

This principle is a crucial part of functional thinking: there are no “variables”, only values .

Functions as values

If you think about it a little longer, you can see that the name " add1" itself is just a link to "a function that increases input by one." The function itself is independent of the name that is tied to it.

Having entered let add1 x = x + 1, we say to the F # compiler "every time you see the name" add1", replace it with a function that adds 1 to the input." “ add1” Is called a function value .

To see that the function does not depend on its name, just execute the following code:

let add1 x = x + 1
let plus1 = add1
add1 5
plus1 5

As you can see, “ add'” and “ plus” are two names attached to the same function.

It is always possible to identify a function-value by its signature, which has a standard form domain -> range. Generalized function-value signature:

val functionName : domain -> range

Simple meanings

Imagine an operation that takes nothing and always returns 5.

This would be a “constant” operation.

How could one describe this in F #? We want to tell the compiler: “every time you see a name c, replace it with 5”. Like this:

let c = 5

When calculating returns:

val c : int = 5

This time there is no matching arrow, just one int. From the new - the equal sign with the real value derived after it. The F # compiler knows that this binding has a known value that will always be returned, namely, the number 5.

In other words, we have just defined a constant, or, in terms of F #, a simple value.
You can always distinguish a simple value from a value function, so all simple values ​​have a similar signature:

val aName: type = constant     // Заметьте - стрелки отсутствуют

Simple values ​​vs. function values ​​| Simple value vs. function-values

It is important to understand that in F #, unlike other languages ​​such as C #, there is a very small difference between simple values ​​and value functions. Both types are values ​​that can be associated with names (using a keyword let), after which they can be transferred everywhere. In fact, we will soon see that the idea that functions are values ​​that can be transferred as input to other functions is one of the key aspects of functional thinking.

Note that there is a slight difference between a simple value and a function value. The function always has domain and range, and must be “applied” to the argument in order to return the result. A simple value does not need to be calculated after binding. Using the example above, if we want to define a "constant function" that returns 5, we could use:

let c = fun()->5    
// or
let c() = 5

The signature of such functions is as follows:

val c : unit -> int

Not so:

val c : int = 5

More information about the unitsyntax of functions and anonymous functions will be given later.

"Values" vs. "Objects"

In functional programming languages ​​such as F #, most things are called “values”. In object-oriented languages ​​like C #, most things are called "objects." What is the difference between "value" and "object"?

The value, as we saw above, is a domain member. Domain of integers, domain of strings, domain of functions that match integers of a string, and so on. In principle, the values ​​are immutable (not changeable). And the values ​​do not have the behavior attached to them.

Objects in the canonical definition are encapsulated data structures with associated behavior (methods). In general, objects must have a state (that is, be changeable), and all operations that change the internal state must be provided by the object itself (through a “dot” -notation).

In F #, even primitive values ​​have a certain amount of "object" behavior. For example, you can get the length of a string through a dot:


But in general, we will avoid the term “object” for standard values ​​in F #, saving it to refer to full-fledged classes, or other values ​​that provide methods.

Naming of values

The standard naming rules used for the names of values ​​and functions are mostly alphanumeric + underscores. But there are a couple of extras:

  1. You can add an apostrophe in any part of the name, excluding the first character.

A'b'c     begin'  // валидные имена

  1. The latter case is often used as a label for “different” versions of a value:

let f = x
let f' = derivative f
let f'' = derivative f'

or for variables of the same name to existing keywords

let if' b t f = if b then t else f

You can also use double quotation marks for any string to make it a valid identifier.

``this is a name``  ``123``    //валидные имена

Cases where you may need to use the double quotation trick:

  • When you need to use an identifier that matches the keyword.

let ``begin`` = «begin»

  • When you need to use natural languages ​​for business rules, unit tests, or BBD style executable Cucumber documentation.

let ``is first time customer?`` = true
let ``add gift to order`` = ()
if ``is first time customer?`` then ``add gift to order``
// Юнит-тест
let [<Test>] ``When input is 2 then expect square is 4``=  
   // code here
// BDD clause
let [<Given>] ``I have (.*) N products in my cart`` (n:int) =  
   // code here

Unlike C #, the F # naming convention requires that functions and values ​​begin with a lowercase letter, and not uppercase ( camelCaserather than PascalCase), unless they are intended to interact with other .NET languages. However, types and modules use capital letters (at the beginning).

Additional resources

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 #:

Several other ways to get started learning F # are also described .

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 that you can freely join . We strongly recommend that you do this!

Do not forget to visit the site of the Russian-speaking community F # ! If you have any questions about learning the language, we will be happy to discuss them in chat rooms:

About authors of translation

Author of the translation @kleidemos
Translation and editorial changes are made by the efforts of the Russian-speaking community of F # -developers . We also thank @schvepsss and @shwars for preparing this article for publication.

Also popular now: