Commits

Eugene Lazin committed 8b7dff4

add OOP section

  • Participants
  • Parent commits 70d9406

Comments (0)

Files changed (1)

 
 \section{Функции}
 Выражения могут быть связаны с именами с помощью оператора связывания - let.
-\begin{quote}
 \begin{ocaml}
 > let value = 42;;
 val value : int = 42
 \end{ocaml}
-\end{quote}
 В данном контексте, value - значение, а не переменная.
 При этом по умолчанию, все значения - неизменяемые.
 Можно создать изменяемое значение, с помощью ключевого слова \emph{mutable}.
-\begin{quote}
 \begin{ocaml}
 > let mutable value = 0
 value <- 42;;
 val mutable value : int = 42
 \end{ocaml}
-\end{quote}
 Существует целый ряд ограничений на использование изменяемых значений.
 К примеру, их нельзя использовать в замыканиях.
 В случае если это необходимо, можно использовать ссылки(reference cells).
 
 Выражения, отображающие одни значения на другие являются функциями.
-\begin{quote}
 \begin{ocaml}
 > let sqr a = a*a;;
 val sqr : int -> int
 > sqr 2;;
 val it : int = 4
 \end{ocaml}
-\end{quote}
 Принципиальной разницы между значениями и функциями нет, функции, это просто значения определенного типа. Их, так-же как и значения, можно использовать в других выражениях, передавать в качестве параметров в другие функции и т.д.
 
 Стоит отметить, что в качестве результата функции используется значение последнего выражения.
 Вывод FSI содержит сигнатуру(тип) функции - int $\to$ int. Это означает, что функция принимает и возвращает значения типа int.
 Сигнатура будет более сложной, если функция принимает большее количество параметров.
-\begin{quote}
 \begin{ocaml}
 > let add a b = a + b;;
 val add : int -> int -> int
 > add 3 5;;
 val it : int = 8
 \end{ocaml}
-\end{quote}
 В данном примере, функция имеет тип - int $\to$ int $\to$ int. Операция $\to$ является ассоциативной. Это означает, что функцию add, можно представить, как функцию, принимающую параметр типа int, и возвращающую функцию типа (int $\to$ int). Можно реализовать функцию add немного иначе, что-бы проиллюстрировать сказанное:
-\begin{quote}
 \begin{ocaml}
 > let add a = (+) a;;
 val add : int -> (int -> int)
 > add 3 5;;
 val it : int = 8
 \end{ocaml}
-\end{quote}
 В данном примере, на самом деле возвращается функция(замыкание) с сигнатурой (int $\to$ int), которая затем применяется к второму аргументу.
 
 \subsection{Вывод типов}
 В роли таки значений, к примеру, могут выступать числовые литералы, так как их тип однозначно определяется суффиксом.
 Поскольку оператор + может работать с разными числовыми типами, при этом нет никакой дополнительной информации о том, как эта функция будет использоваться, компилятор, по умолчанию, приводит ее к типу \emph{int $\to$ int $\to$ int}.
 Это можно проиллюстрировать на примере такой сессии:
-\begin{quote}
 \begin{ocaml}
 > let add a b = a + b
 in add 2.0 3.0;;
 val add : float -> float -> float
 \end{ocaml}
-\end{quote}
 В данном примере, функция add имеет тип \emph{float $\to$ float $\to$ float}, так как компилятор может вывести ее тип на основе того, как она используется.
 Помимо прочего, F\# позволяет использовать аннотации типов, для того, что-бы явно указать тип возвращаемого значения или параметра функции.
-\begin{quote}
 \begin{ocaml}
 > let add (a:float) (b:float) = a + b;;
 val add : float -> float -> float
 \end{ocaml}
-\end{quote}
 В данном примере, типы аргументов функции заданы явно, с помощью аннотаций.
 Указывать типы всех аргументов - не обязательно, достаточно указать тип любого из них, или тип возвращаемого значения функции, как в следующем примере:
-\begin{quote}
 \begin{ocaml}
 > let add a b : float = a + b;;
 val add : float -> float -> float
 \end{ocaml}
-\end{quote}
 
 \subsection{Обобщенные функции}
 Если механизм вывода типов не может сделать никаких предположений относительно того, какой тип имеет аргумент функции, такая функция становится обобщенной.
 Это значит, что хотя-бы один из ее аргументов может иметь произвольный тип.
-\begin{quote}
 \begin{ocaml}
 > let toStr a = a.ToString();;
 val toStr : 'a -> string
 \end{ocaml}
-\end{quote}
 В данном примере, параметр a функции toStr, может принимать значения произвольного типа.
 Имена обобщенных типов начинаются с апострофа, за которым следует имя идентификатора\footnote{обычно используются латинские символы в нижнем регистре - 'a, 'b\ldots}.
 Такие имена можно использовать в аннотациях типов.
 \subsection{Анонимные функции}
 F\# позволяет создавать функции, не связанные с именами.
 Такие функции называются анонимными и создаются с помощью ключевого слова fun.
-\begin{quote}
 \begin{ocaml}
 > fun x -> x**0.5;;
 val it : float -> float = <fun:clo@10>
 \end{ocaml}
-\end{quote}
 
 
 \subsection{Замыкания}
 Замыкание - это функция, способная использовать локальные переменные из того контекста, в котором она определена.
-\begin{quote}
 \begin{ocaml}
 > let add a = fun b -> a + b;;
 val add : int -> int -> int
 \end{ocaml}
-\end{quote}
 В данном примере, анонимная функция, возвращающая сумму своего аргумента и аргумента функции add - является замыканием.
 Параметр функции add, будет доступен в анонимной функции, несмотря на то, что он туда не передается явно.
 
 Частичное применение функций, или карринг(анг. currying) - преобразование, позволяющее свести любую функцию, к функции одного аргумента.
 F\# в полной мере поддерживает данное преобразование на уровне языка.
 Это можно продемонстрировать на простом примере:
-\begin{quote}
 \begin{ocaml}
 > let sum3 x y z = x + y + z;;
 val sum3 : int -> int -> int -> int
 > let sum2 = sum3 0;;
 val sum2 : (int -> int -> int)
 \end{ocaml}
-\end{quote}
 Функция sum3 - принимает три аргумента, указав только один из них, мы получили функцию, принимающую только два аргумента.
 
 Любую функцию множества аргументов, можно представить как функцию одного аргумента, возвращающую другую функцию с помощью функций высшего порядка.
-\begin{quote}
 \begin{ocaml}
 > let csum3 = fun x -> fun y -> fun z -> x + y + z;;
 val csum3 : int -> int -> int -> int
 \end{ocaml}
-\end{quote}
 Функция csum3 - возвращает функцию, возвращающую другую функцию, которая в свою очередь возвращает еще одну функцию\footnote{эта функция является замыканием, так как "помнит" свое окружение, а именно - аргументы других функций}. 
 Последняя функция возвращает результат.
 Следует обратить внимание на тип функции, он не отличается от типа функции sum3.
 Pipe-forward оператор, может избавить программиста, от необходимости передавать промежуточные результаты вручную.
 
 Данный оператор определяется следующим образом:
-\begin{quote}
 \begin{ocaml}
 let (|>) x f = f x
 \end{ocaml}
-\end{quote}
 На первый взгляд, эта функция не очень полезна.
 Мы просто поменяли местами функцию и ее параметр, вместо f x, можем теперь писать x |> f.
 Это дает две новые возможности.
 \begin{itemize}
 \item Во первых, это позволяет избавиться от необходимости явного использования промежуточных результатов в вычислениях.
 Подобный код:
-\begin{quote}
 \begin{ocaml}
 let a = first input
 let b = second a
 let result = third b
 \end{ocaml}
-\end{quote}
 Можно сделать более простым и интуитивным\footnote{однако это может усложнить отладку, так как промежуточные результаты вычисления теперь недоступны}:
-\begin{quote}
 \begin{ocaml}
 let result = input |> first |> second |> third
 \end{ocaml}
-\end{quote}
 Совершенно очевидно, что второй вариант читать проще.
 \item Помимо этого, применение данного оператора, может упростить вывод типов.
 Компилятор обрабатывает код сверху вних и слева направо.
 Поэтому, вот этот код, не будет компилироваться, так как во время обработки функции toLower, еще неизвестен тип ее аргумента.
-\begin{quote}
 \begin{ocaml}
 > let toLower str = fun x -> x.ToLower()
 toLower "This code is wrong and ugly!";;
 \end{ocaml}
-\end{quote}
 Что-бы этот код заработал, необходимо добавить явную аннотацию типа.
 
 Однако можно поступить по другому:
-\begin{quote}
 \begin{ocaml}
 > "This code is much better" |> (fun x -> x.ToLower());;
 val it : string = "this code is much better!"
 \end{ocaml}
-\end{quote}
 Теперь тип параметра функции известен заранее, поэтому явная аннотация типа не нужна.
 \end{itemize}
 
 Данный оператор похож на pipe-forward оператор, его отличие состоит в том, что он не применяет один параметр к другому.
 Вместо этого, он возвращает новую функцию, являющуюся композицией двух других.
 Данный оператор реализован следующим образом:
-\begin{quote}
 \begin{ocaml}
 let (>>) f g x = g(f x)
 \end{ocaml}
-\end{quote}
 Этот оператор находит применение в тех же случаях, что и pipe-forward оператор, но применяется он тогда, когда требуется получить функцию, а не значение.
 Рассмотрим следующий пример:
-\begin{quote}
 \begin{ocaml}
 > let calcResult input = input |> first |> second |> third;;
 val calcResult : int -> int
 \end{ocaml}
-\end{quote}
 Данная функция вычисляет результат следующим образом - third(second (first input))).
 С помощью оператора (>>), можно записать это немного проще:
-\begin{quote}
 \begin{ocaml}
 > let calcResult = first >> second >> third;;
 val calcResult : int -> int
 \end{ocaml}
-\end{quote}
 
 \subsubsection{Pipe-backward operator}
 Этот оператор определяется следующим образом:
-\begin{quote}
 \begin{ocaml}
 let (<|) f x = f x
 \end{ocaml}
-\end{quote}
 На первый взгляд он не кажется полезным, однако это не так.
 Данный оператор служит для изменения приоритета вычисления операндов.
 Это можно пояснить на простом примере:
-\begin{quote}
 \begin{ocaml}
 > printfn "%s" (sprintf "value = %d" 42);;
 value = 42
 val it : unit = ()
 \end{ocaml}
-\end{quote}
 Без круглых скобок, вокруг второго параметра, этот код работать не будет, так как компилятор будет считать, что вторым параметром функции printfn, является функция snprintf.
 Что-бы этого избежать, можно воспользоваться оператором (<|).
-\begin{quote}
 \begin{ocaml}
 > printfn "%s" <| sprintf "value = %d" 42;;
 value = 42
 val it : unit = ()
 \end{ocaml}
-\end{quote}
 
 \subsubsection{Backward composition operator}
 Данный оператор является аналогом оператора (<|), с той лишь разницей, что он не вычисляет результат, а просто выполняет композицию функций.
-\begin{quote}
 \begin{ocaml}
 let (<<) f g x = f (g x)
 \end{ocaml}
-\end{quote}
 Пример использования оператора:
-\begin{quote}
 \begin{ocaml}
 > let printValue = printfn "%s" << sprintf "value = %d";;
 val printValue : (int -> unit)
 value = 42
 val it : unit = ()
 \end{ocaml}
-\end{quote}
 Этот оператор, так-же как pipe-backward operator, помогает сделать код более ясным и читаемым.
 
 
 В дальнейшем, во время компиляции, будет выполняться проверка корректности приложения, на основе анализа размерностей.
 
 Для создания единицы измерения, нужно указать атрибут Measure, перед объявлением типа\footnote{сам тип может иметь только статические методы}.
-\begin{quote}
 \begin{ocaml}
 > [<Measure>] type m
 [<Measure>] type s;;
 \end{ocaml}
-\end{quote}
 После этого, можно использовать типы s и m, в качестве единиц измерения, для этого нужно указать один из них в угловых скобках, после соответствующего значения или типа. 
-\begin{quote}
 \begin{ocaml}
 > let a = 10<m>;;
 val a : int<m> = 10
 > let b = 2<s>;;
 val b : int<s> = 2
 \end{ocaml}
-\end{quote}
 Значения a и b имеют одинаковые типы, но разные размерности. 
 Вычитать, складывать и сравнивать можно только значения, имеющие одну размерность.
 Значения разных размерностей можно умножать и делить.
 При этом результат будет иметь составную размерность.
-\begin{quote}
 \begin{ocaml}
 > let c = a + b;;
 \end{ocaml}
-\end{quote}
 Попытка сложить метры с секундами приводит к ошибке - \emph{<<error FS0001: The unit of measure 's' does not match the unit of measure 'm'>>}.
-\begin{quote}
 \begin{ocaml}
 > let v = a/b;;
 val v : int<m/s> = 5
 \end{ocaml}
-\end{quote}
 Значение v имеет составную размерность m/s.
 В предыдущем примере, единицы измерения m и s являются независимыми, то есть не выражаются друг через друга.
 F\# позволяет определять зависимости между разными единицами измерения.
-\begin{quote}
 \begin{ocaml}
 > [<Measure>] type Hz = s ^ -1;;
 \end{ocaml}
-\end{quote}
 или
-\begin{quote}
 \begin{ocaml}
 > [<Measure>] type Hz = 1/s;;
 \end{ocaml}
-\end{quote}
 Значения, имеющие размерность Hz, можно будет использовать в качестве значений с размерностью 1/s.
 
 \subsubsection{Обобщенные единицы измерения}
 Обычные функции, работающие с числовыми значениями разных типов, не будут работать со значениями, имеющими размерность.
 Это может быть неудобно, так как требует использовать функции приведения типа.
-\begin{quote}
 \begin{ocaml}
 > let sqr a = a*a;;
 val sqr : int -> int
 > sqr (int a);;
 val it : int = 4
 \end{ocaml}
-\end{quote}
 Что-бы избежать ненужных преобразований, можно не указывать единицу измерения явно.
 Для этого, нужно вместо размерности, использовать символ подчеркивания.
 Механизм вывода типов определит используемую размерность автоматически, иначе она будет обобщенной.
-\begin{quote}
 \begin{ocaml}
 > let sqr (a : int<_>) = a*a;;
 val sqr : int<'u> -> int<'u ^ 2>
 > sqr a;;
 val it : int<s ^ 2> = 4
 \end{ocaml}
-\end{quote}
 В данном примере, функция sqr принимает параметр типа int, имеющий любую размерность, либо не имеющий ее вовсе.
 
 \section{Встроенные типы}
 Элементы кортежа могут иметь разные типы.
 
 Для создания нового экземпляра кортежа\footnote{следует иметь ввиду, что экземпляры класса Tuple всегда создаются в куче, в случае, если это недопустимо, нужно использовать структуру}, нужно перечислить значения входящие в него, через запятую.
-\begin{quote}
 \begin{ocaml}
 > let tuple = "first element", 2, 3.0;;
 
 val tuple : string * int * float = ("first element", 2, 3.0)
 \end{ocaml}
-\end{quote}
 В данном примере \emph{string * int * float} - это тип кортежа.
 Существуют две встроенные функции для работы с кортежами - fst и snd, возвращающие первый и второй элемент кортежа.
 Эти функции определены только для кортежей, состоящих из двух элементов.
 Для извлечения элементов из кортежа можно использовать оператор связывания.
 Для этого, в левой части, через запятую, должны быть перечислены идентификаторы, соответствующие элементам кортежа.
-\begin{quote}
 \begin{ocaml}
 > let first, second, third = tuple;;
 
 val second : int = 2
 val first : string = "first element"
 \end{ocaml}
-\end{quote}
 
 \subsection{Список}
 Список, это цепочка, состоящая из элементов одного типа.
 Для создания нового списка, нужно перечислить его элементы в квадратных скобках, через точку с запятой.
-\begin{quote}
 \begin{ocaml}
 > let lst = [ 1; 3; 6; 10; 15 ];;
 val lst : int list = [1; 3; 6; 10; 15]
 \end{ocaml}
-\end{quote}
 Если в скобках не указывать элементы, список будет иметь тип - \emph{'a list}.
 
 \subsubsection{Более подробно о создании списков}
 Для того, что-бы создать новый список, вовсе не обязательно перечислять все его элементы.
 Существуют другие возможности - создание списка на основе диапазона значений, а так-же генераторы списков\footnote{в англоязычной литературе используется термин - list comprehensions, чаще всего используется именно этот термин}.
 Для создания списка, содержащего диапазон значений, нужно задать его верхнюю и нижнюю границу:
-\begin{quote}
 \begin{ocaml}
 > let a = [1 .. 10];;
 val a : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
 \end{ocaml}
-\end{quote}
 Так-же, существует возможность указать шаг приращения значений\footnote{шаг может быть отрицательным}.
-\begin{quote}
 \begin{ocaml}
 > let a = [1 .. 2 .. 10];;
 val a : int list = [1; 3; 5; 7; 9]
 \end{ocaml}
-\end{quote}
 
 В более сложных случаях, можно использовать генераторы списков.
 Генератор списка - это фрагмент кода, заключенный в квадратные скобки, используемый для создания всех элементов списка.
 Элементы списка, добавляются с помощью ключевого слова yield.
 С помощью такого выражения, можно получить список четных чисел:
-\begin{quote}
 \begin{ocaml}
 > let evenlst = [
     for i in 1..10 do
 
 val evenlst : int list = [2; 4; 6; 8; 10]
 \end{ocaml}
-\end{quote}
 Практически любой F\# код, можно использовать внутри генераторов списков\footnote{list comprehensions - это частный случай использования такой возможности языка, как computation expressions, о которой будет рассказано ниже}.
 
 \subsubsection{Операции над списками}
 Списки - неизменяемы, добавить или удалить элемент из списка - нельзя.
 Вместо этого, можно создать новый список, на основе существующего.
 Для добавления элемента в начало списка служит оператор <<::>>.
-\begin{quote}
 \begin{ocaml}
 > let newlst = 0 :: lst;;
 val newlst : int list = [0; 1; 3; 6; 10; 15]
 \end{ocaml}
-\end{quote}
 Который является алиасом функции Cons из модуля List:
-\begin{quote}
 \begin{ocaml}
 > let newlst = List.Cons(0, lst);;
 val newlst : int list = [0; 1; 3; 6; 10; 15]
 \end{ocaml}
-\end{quote}
 Модуль List - содержит множество функций, работающих со списками.
 Два списка можно объединить, с помощью оператора конкатенации - <<@>>.
-\begin{quote}
 \begin{ocaml}
 > let cclst = lst @ [21; 28];;
 val cclst : int list = [1; 3; 6; 10; 15; 21; 28]
 \end{ocaml}
-\end{quote}
 
 Функции List.Head и List.Tail, возвращают первый элемент и хвост списка соответственно.
 
 List.map - создает новый список, применяя пользовательскую функцию ко всем элементам списка.
 Ее тип - ('a $\to$ 'b) $\to$ 'a list $\to$ 'b list.
 Тоесть, она принимает на вход пользовательскую функцию и список, и возвращает новый список.
-\begin{quote}
 \begin{ocaml}
 > List.map (fun i -> i*i) [1; 2; 3; 4];;
 val it : int list = [1; 4; 9; 16]
 \end{ocaml}
-\end{quote}
 В данном примере, пользовательская функция возвращает квадрат элемента, переданного на ее вход\footnote{помимо прочего, она является анонимной, анонимные функции создаются с помощью ключевого слова fun, после которого перечисляются параметры анонимной функции, после $\to$ записывается тело функции}.
 
 List.reduce - выполняет операцию свертки.
 Если посмотреть на тип этой функции - ('a $\to$ 'a $\to$ 'a) $\to$ 'a list $\to$ 'a, можно увидеть, что она отличается от List.map типом функции - параметра, а так-же тем, что возвращает единственное значение, а не список.
 Пользовательская функция, принимает на вход два параметра - аккумулятор и элемент списка, и должна вернуть новое значение аккумулятора.
-\begin{quote}
 \begin{ocaml}
 > List.reduce (fun acc i -> i + acc) [1; 2; 3; 4; 5];;
 val it : int = 15
 \end{ocaml}
-\end{quote}
 В данном примере, с помощью функции List.reduce, вычисляется сумма всех элементов списка\footnote{функция List.sum позволяет сделать тоже самое намного проще}.
 В первый раз, функция, переданная пользователем получает первый элемент списка в качестве аккумулятора.
 
 В отличии от List.reduce, она содержит один дополнительный параметр.
 Этот параметр выступает в роли начального значения аккумулятора.
 Это можно проиллюстрировать на примере выражения, преобразующего список чисел в строку.
-\begin{quote}
 \begin{ocaml}
 let lst = [1; 2; 3; 4]
 
 
 val it : string = "1, 2, 3, 4"
 \end{ocaml}
-\end{quote}
 Поскольку тип результата отличается от типа элементов списка, использовать функцию List.reduce в данном случае нельзя.
 Итак, первый параметр List.fold - функция, получающая на вход два параметра - строку и число.
 Эта функция, прибавляет к строке--аккумулятору запятую, пробел и строковое представление числа - элемента массива.
 
 \subsection{Тип данных Lazy}
 С помощью значения данного типа, можно представить отложенное вычисление.
-\begin{quote}
 \begin{ocaml}
 > let lv = Lazy<_>.Create(fun () -> printfn "Eval"; 42);;
 val lv : System.Lazy<int> = <unevaluated>
 > lv.Value;;
 val it : int = 42
 \end{ocaml}
-\end{quote}
 При первом обращении к свойству Value, происходит вычисление значения.
 При последующих - используется ранее вычисленное значение.
 Так-же, можно использовать ключевое слово lazy.
-\begin{quote}
 \begin{ocaml}
 > let lv = lazy(printfn "Eval"; 42);;
 val lv : Lazy<int> = <unevaluated>
 \end{ocaml}
-\end{quote}
 
 \subsection{Последовательности}
 Последовательности - аналогичны спискам, за тем исключением, что только один элемент последовательности существует в данный момент времени.
 
 Последовательности создаются похожим со списками образом.
 Можно задать последовательность в виде диапазона:
-\begin{quote}
 \begin{ocaml}
 > let intseq = seq {1 .. System.Int32.MaxValue};;
 val intseq : seq<int>
 \end{ocaml}
-\end{quote}
 Попытка создать список, такого размера, приведет к ошибке.
 
 Последовательность - всего лишь алиас, для .NET интерфейса IEnumerable<\_>, по этой причине, последовательности легко могут быть использованы из сборок, написанных на других .NET языках.
 Выполнение кода прерывается после того, как очередной элемент последовательности был создан с помощью ключевого слова yield.
 Дальнейшее выполнение кода будет продолжено тогда, когда потребуется следующий элемент.
 Код генератора последовательности записывается в фигурных скобках, расположенными после идентификатора seq.
-\begin{quote}
 \begin{ocaml}
 let sumbols = seq {
     for c in 'A' .. 'Z' do
         yield System.Char.ToLower(c)
 }
 \end{ocaml}
-\end{quote}
 
 В отличии от генераторов списков, генераторы последовательностей могут быть вложенными, а также - рекурсивными.
 Для того, что-бы включить в одну последовательность, элементы другой последовательности, нужно использовать ключевое слово yield!\footnote{yield bang}.
-\begin{quote}
 \begin{ocaml}
 let rec collatz n = seq {
     yield n   
 }
 
 \end{ocaml}
-\end{quote}
 В данном примере, функция collatz - возвращает все числа, входящие в последовательность Коллаца, начиная с n.
 Во первых, функция является рекурсивной\footnote{в таких случаях, обязательно нужно использовать ключевое слово rec}, она вызывает сама себя, для получения очередного значения последовательности.
 Во вторых, функция возвращает последовательность, ее тело состоит из одного sequence expression.
 После каждого правила, следует значение выражения, которое принимает выражение в том случае, если образец соответствует этому правилу.
 Все правила сопоставления с образцом, должны возвращать значения одного и того же типа.
 В простейшем случае, в качестве правил могут выступать постоянные значения:
-\begin{quote}
 \begin{ocaml}
 > let xor x y =
     match x, y with
 ;;
 val xor : bool -> bool -> bool
 \end{ocaml}
-\end{quote}
 В правилах сопоставления с образцом, можно использовать wildcard - символ <<\_>>, в том случае, если нас не интересует конкретное значение.
-\begin{quote}
 \begin{ocaml}
 > let testAnd x y =
     match x, y with
 > testAnd false false;;
 val it : bool = false
 \end{ocaml}
-\end{quote}
 
 В том случае, если набор правил сопоставления - не покрывает все возможные значения образца, компилятор F\# выдает предупреждение.
 Если во время сопоставления с образцом, ни одно правило не будет соответствовать образцу - будет брошено исключение - MatchFailureException.
 
 Помимо констант, в правилах сопоставления с образцом можно использовать имена значений.
 Это нужно для того, что-бы извлечь данные из образца и связать их с именем.
-\begin{quote}
 \begin{ocaml}
 > let printValue x =
     match x with
     | 0 -> printfn "is zero"
     | a -> printfn "is %d" a;;
 \end{ocaml}
-\end{quote}
 Конечно, данный пример выглядит сильно надуманным, так-как в выражении можно использовать значение---образце непосредственно.
 В данном примере, в последнем правиле можно использовать вместо именованного значения шаблон wildcard.
 Однако, это может быть очень удобно в тех случаях, когда мы имеем дело с более сложными данными, например с кортежами, или списками.
 В этом случае, с помощью правила сопоставления с образцом, можно разобрать сложную структуру данных на составные части.
 
 Разные правила могут быть объединены между собой с помощью логических операций.
-\begin{quote}
 \begin{ocaml}
 > let testOr x y =
     match x, y with
 
 val testOr : bool -> bool -> bool
 \end{ocaml}
-\end{quote}
 В данном примере, первое правило сработает в том случае, если один из аргументов равен true.
 
 В некоторых случаях, простого сопоставления бывает мало, требуется использовать более сложную логику в правилах.
 В таких случаях можно использовать дополнительные ограничения, которые можно указать после ключевого слова when.
-\begin{quote}
 \begin{ocaml}
 > let testOr x y =
     match x, y with
     | _ when y = true -> true
     | _ -> false;;
 \end{ocaml}
-\end{quote}
 
 Помимо всего прочего, механизм сопоставления с образцом позволяет разбирать сложные данные, такие как кортежи, списки, размеченные множества\ldots
 
 \subsection{Сопоставление с образцом без использования match expression}
 Сопоставление с образцом - фундаментальная возможность языка, которая используется повсеместно.
 Помимо match выражений, сопоставление с образцом может выполняться в операторе связывания(let binding).
-\begin{quote}
 \begin{ocaml}
 > let t = "first tuple element", 2
 let _, b = t;;
 val t : string * int = ("first tuple element", 2)
 val b : int = 2
 \end{ocaml}
-\end{quote}
 В данном примере, с помощью первого оператора связывания создается кортеж из двух элементов.
 С помощью второго оператора связывания извлекается второй элемент кортежа.
 
 Помимо этого, сопоставление с образцом выполняется в анонимных функциях.
 Предидущий пример можно переписать следующим образом:
-\begin{quote}
 \begin{ocaml}
 > let f = fun (_, second) -> printfn "%d" second
 f("first tuple element", 2);;
 
 val f : 'a * int -> unit
 \end{ocaml}
-\end{quote}
 Следует отметить, что в данном случае, правило сопоставления с образом - одно, поэтому оно должно быть полным, подходить для любого образца. 
 Компилятор не позволит написать что-либо подобное:
-\begin{quote}
 \begin{ocaml}
 > let lst = [1..10]
 let head :: _ = lst;;
 \end{ocaml}
-\end{quote}
 Так-как в этом случае, правило \emph{head :: \_} будет подходить только для не пустого списка.
 
 \subsection{Active patterns}
 Сопоставление с образцом - намного более выразительный механизм, чем обычные условные выражения.
 Однако их не всегда удобно использовать, к примеру, в правилах сопоставления с образцом нельзя вызывать функции, поэтому в таких случаях, нужно использовать when guard.
 Рассмотрим следующий пример:
-\begin{quote}
 \begin{ocaml}
 open System.IO
 
         -> Application
     | _ -> Unknown
 \end{ocaml}
-\end{quote}
 Сопоставление с образцом здесь не дает каких-либо преимуществ, по сравнению с обычным сравнением.
 
 Для восполнения этого и подобных пробелов, служат активные шаблоны(active patterns) - особые функции, которые могут быть использованы в правилах сопоставления с образцом.
 Активный шаблон этого типа - простая функция одного аргумента, заключенная в прямые, а затем в круглые скобки\footnote{banana clips}.
 
 Предидущий пример, можно переписать следующим обрбазом:
-\begin{quote}
 \begin{ocaml}
 open System.IO
 
     | FileExtension ".exe" -> Application
     | _ -> Unknown
 \end{ocaml}
-\end{quote}
 Здесь FileExtension - активный шаблон, который просто возвращает расширения файла, за именем шаблона следует его результат.
 Если результат представлен константой, то происходит сравнение результата со значением константы и в случае, если оно успешно, правило считается выполненым.
 В случае, если результат - именованое значение, правило считается выполненым, а результат выполнения функции---активного шаблона можно получить используя имя результата.
 \end{ocaml}
 Нам больше не нужна функция parseStr, которая занималась разбором строки и возвращала элемент размеченного множества.
 
-\section{Объектно ориентированное программирование}
+\section{Пользовательские типы данных}
+F\# поддерживает различные пользовательские типы данных, такие, как записи, размеченные объединения, классы.
+Помимо этого, в полной мере поддерживается параметричесский полиморфизм.
+Пользовательские типы могут быть обобщенными, также как и функции.
 
+Механизм вывода типов работает с пользовательскими типами данных, избавляя от необходимости явно аннотировать типы в большинстве случаев.
+\begin{ocaml}
+> type Person = { Name : string; Age : int };;
+
+type Person =
+  {Name: string;
+   Age: int;}
+
+> let mary = { Name = "Mary"; Age = 22 };;
+
+val mary : Person = {Name = "Mary";
+                     Age = 22;}
+\end{ocaml}
+В этой сессии FSI, мы не указывали тип значения mary явно, компилятор смог вывести его самостоятельно, на основе того, какие поля использовались при создании этой записи.
+Тоже саоме справедливо и для размеченных объединений:
+\begin{ocaml}
+type List<'a> =
+     | Cons of 'a * List<'a>
+     | Nil
+
+> let lst = Cons(10, Cons(20, Cons(30, Nil)));;
+
+val lst : List<int> = Cons (10,Cons (20,Cons (30,Nil)))
+\end{ocaml}
+Тип, при создании списка не фигурирует, тем не менее, механизм вывода типов корректно его определяет.
+В случае, если это не возможно, следует использовать полное имя соответствующего элемента объединения:
+\begin{ocaml}
+> let lst = List.Cons("first", List.Nil);;
+
+val lst : List<string> = Cons ("first", Nil)
+\end{ocaml}
+Помимо этого, в данном примере, тип List - обобщенный, он может содержать элементы произвольного типа.
+
+\subsection{Объекты и классы}
+F\# поддерживает два способа объявления классов - явный и неявный.
+Первый подходит в тех случаях, когда программисту требуется контроль над тем, как создается объект класса и какие поля он содержит.
+Второй позволяет переложить большую часть работы на компилятор.
+
+В случае явного объявления класса, программист должен объявить поля класса и хотя-бы один конструктро:
+\begin{ocaml}
+type Book =
+
+    val title : string
+    val author : string
+    val publishDate : DateTime
+    
+    new (t, a, pd) = {
+        title = t
+        author = a
+        publishDate = pd
+    }
+\end{ocaml}
+С помощью ключевого слова val - объявляются члены-данные класса, а с помощью ключевго слова new - создается конструктор класса.
+Члены-данные класса должны в обязательном порядке инициализироваться в конструкторе, точнее внутри constructor expression, которое записываетсяв фигурных скобках, позсле символа равенства.
+В случае, если какое-либо поле не будет инициализировано внутри constructor expression, компилятор выдаст ошибку.
+Внутри onstructor expression можно только инициализировать поля класса, причем каждое поле - только один раз, попытка сделать что-нибудь еще приведет к обшибке компиляции.
+
+На первый взгляд, это ограничение кажется неразумным, так как иногда все же требуется выполнить какие-либо действия во время создания экземпляра класса.
+Например выполнить валидацию параметров конструктора, или записать что-нибудь в лог.
+На самом деле все не так печально, в конструкторе можно не только инициализировать члены-данные, можно добавить произвольный код, который будет выполняться до инициализации - он записывается перед constructor expression, а также код, который будет выполняться после инициализации полей - он записывается после constructor expression\footnote{в этом случае, после закрывающей фигурной скобки, должно следовать ключевое слово then}.
+\begin{ocaml}
+type Book =
+
+    val title : string
+    val author : string
+    val publishDate : DateTime
+    
+    new (t:string, a:string, pd) = 
+    
+        if t.Length = 0 then 
+            failwithf "Book title is empty (%s, %A)" a pd
+            
+        if a.Length = 0 then
+            failwithf "Book author is empty (%s, %A)" t pd
+            
+        {
+            title = t
+            author = a
+            publishDate = pd
+        }
+        then
+            printfn "New book is constructed %s %s %A" t a pd
+\end{ocaml}
+В данном примере, перед выполнением constructor expression выполняется проверка переданных параметров, в случае, если они не проходят проверку - генирируется исключение.
+После выполнения constructor expression - на экран выводится сообщение.
+Следует обратить внимание на то, что код конструктора не может обращаться к членам-данным объекта иначе, чем через constructor expression.
+
+Все члены-данные, по умолчанию - неизменяемы.
+Также, как и в случае обычных значений, это поведение можно изменить с помощью ключевого слова \emph{mutable}.
+
+Во многих случаях, классы можно объявлять намного проще.
+Для этого, нужно сразу после имени класса, в круглых скобках, перечислить параметры его основного конструктора.
+Эти параметры будут доступны для использования в методах класса.
+При этом объявлять их явно с помощью val-binding не нужно.
+\begin{ocaml}
+type Book(title:string, author:string, publishDate:DateTime) =
+            
+    member this.Title = title
+    
+    member this.Author = author
+\end{ocaml}
+В этом случае, конструктор класса\footnote{этот конструктор, является основным конструктором класса}, а также его члены-данные, создаются неявно.
+В данном примере, конструктор класса будет принимать три параметра, но сам класс будет иметь два поля - author и title.
+Поле publishDate создано не будет, так-как нигде не используется.
+Попытка использовать val-binding в классе, объявленом с использованием неявного синтаксиса - приведт к ошибке компиляции.
+
+Класс, объявленый неявно, может содержать конструкторы.
+Эти конструкторы, также объявляются с помощью ключевого слова new, за которым следует список параметров и код конструктора.
+Но, в отличии от явного объявления класса, в этом случае мы не можем использовать constructor expression.
+Вместо него, конструктор обязательно должен вызывать основной конструктор\footnote{тот, который создается компилятором} и передавать в него соответствующие параметры.
+\begin{ocaml}
+type Book(title:string, author:string, publishDate:DateTime) =
+            
+    member this.Title = title
+    
+    member this.Author = author
+    
+    new (str:string) =
+        let parts = str.Split(';')
+        let title = parts.[0]
+        let author = parts.[1]
+        let publishDate = DateTime.Parse(parts.[2])
+        new Book(title, author, publishDate)
+        then
+            printfn "New book is created from string - %s" str
+
+\end{ocaml}
+В этом примере, определено дополнительный конструктор, который получает на вход строку, разбирает ее и вызывает основной конструктор.
+
+Внутри неявного определения класса можно объявлять переменные, используя оператор связывания \emph{let} и выполнять произвольный код, с помощью ключевого слова \emph{do}.
+\begin{ocaml}
+type Book(title:string, author:string, publishDate:DateTime) =
+
+    do if title.Length = 0 then 
+        failwithf "Book title is empty (%s, %A)" author publishDate
+            
+    do if author.Length = 0 then
+        failwithf "Book author is empty (%s, %A)" title publishDate
+           
+    let mutable publishDate = publishDate
+    
+    member this.Title = title
+    
+    member this.Author = author
+    
+    member this.SetPublishDate(newdate:DateTime) =
+        publishDate <- newdate
+    
+    new (str:string) =
+        let parts = str.Split(';')
+        let title = parts.[0]
+        let author = parts.[1]
+        let publishDate = DateTime.Parse(parts.[2])
+        new Book(title, author, publishDate)
+        then
+            printfn "New book is constructed from string %s" str
+\end{ocaml}
+Здесь добавлены проверки для параметров основного конструктора, которые будут выполняться во время создания объекта - независимо от того, с помощью какого конструктора объект создается.
+Помимо этого, с помощью оператора связывания, здесь объявлена переменная класса publishDate, а также метод класса - SetPublishDate, с помощью которого можно изменять значение этой переменной.
+
+Классы, также как записи или размеченные объединения, могут быть обобщенными.
+Для этого, нужно после имени класса, в фигурных скобках, перечислить обобщенные параметры класса.
+\begin{ocaml}
+type Point<'a>(x:'a, y:'a) =
+    member this.X = x
+    member this.Y = y
+\end{ocaml}
+Либо просто не указывать типы параметров конструктора.
+\begin{ocaml}
+type Point(x, y) =
+    member this.X = x
+    member this.Y = y
+\end{ocaml}
+Создать экземпляр класса можно следующим образом:
+\begin{ocaml}
+> let p = Point<_>(1.0, 1.0);;
+val p : Point<float>
+\end{ocaml}
+Если не указывать тип явно или не использовать wildcard, для того, что-бы позволить механизму вывода типов сделать свою работу, компилятор будет использовать тип object.
+\begin{ocaml}
+> let p = Point(10, 20);;
+val p : Point
+let x:int = p.X;;
+\end{ocaml}
+Этот код не будет компилироваться, так как p.X будет иметь тип System.Object.
+
+\subsection{Свойства и методы}
+В предидущих примерах, методы уже использовались, теперь настало время поговорить о них подробнее.
+Объявление метода или свойства начинается с ключевого слова member, за которым следует имя self-идентификатора.
+Это имя используется для обращения к членам класса внутри метода или свойства.
+В случае, если метод или свойство - статические, self-идентификатор указывать не нужно.
+\begin{ocaml}
+type Point<'a>(x:'a, y:'a) =
+
+    let mutable m_x = x
+    let mutable m_y = y
+    
+    member this.Reset(other:Point<_>) =
+        m_x <- other.X
+        m_y <- other.Y
+    
+    member this.X with get() = m_x 
+                   and set x = m_x <- x
+                   
+    member this.Y with get() = m_y
+                   and set y = m_y <- y
+
+\end{ocaml}
+Здесь, класс Point имеет два свойства и один метод.
+Свойства X и Y, доступны для чтения и для записи.
+При объявлении совйства, можно не указывать get, либо set функцию, в этом случае, свойство будет недоступно для чтения, либо для записи.
+
+Видимость методов, свойств, полей и классов, можно изменять с помощью ключевых слов public, private и internal.
+В отличии от других .NET языков, F\# не поддерживает модификатор protected, однако правильно работает с protected методами и свойствами классов, созданных с использованием других языков программирования.
+
+В F\# полностью поддерживается перегрузка методов.
+Можно определить несколько методов, имеющих одинаковое имя, но отличающихся типом и количеством параметров.
+
+\subsection{Наследование}
+
+\subsection{Object expressions}
+
+\subsection{Методы расширения}
 
 \end{document}