Характерные черты функциональных языков...

Preview:

DESCRIPTION

 

Citation preview

ХАРАКТЕРНЫЕ ЧЕРТЫ ФУНКЦИОНАЛЬНЫХ ЯЗЫКОВ ПРОГРАММИРОВАНИЯAlex Kolonitsky

ЧЕГО ОЖИДАТЬ.

Будут Идеи функционального

программирования. Примеры на языке Haskell /Scala Сравнение функциональных языков и

императивных.

Не будет Разбора математической теории,

лежащей в основе функционального программирования.

Полного описания языка Haskell/Scala2

СОДЕРЖАНИЕ Введение Вывод типов Функции

Функция высшего порядка Замыкание Частичное применение и карринг Функции вместо объектов

Algebraic data type Сопоставление с образцом Ленивые Вычисления Неизменяемое состояние Benefits 3

ПРОГРАММИРОВАНИЕ...

Императивное – мы говорим компьютеру, как решать задачу (что делать по шагам). Основной акцент – манипулирование ячейками

памяти Функции как способ декомпозиции задачи на более

простые

Функциональное - мы говорим компьютеру, что решать (описание задачи). Выполнение программы рассматривается как вычисление математических функций (выражений). Мы описываем функции, работающие над данными –

система программирования решает, как их вычислять!

4

Введение

ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ

5

Введение

КОНТРОЛЬ ТИПОВ

По времени выполнения Статическая типизация — контроль типов

осуществляется во время компиляции. Динамическая типизация — контроль типов

осуществляется во время выполнения.

…может быть строгим и слабым Строгая типизация — совместимость типов

автоматически контролируется транслятором: Слабая типизация — совместимость типов никак

транслятором не контролируется. В языках со слабой типизацией обычно используется подход под названием «утиная типизация».

6

Вывод типов

ТИПИЗАЦИЯ (ДОПОЛНЯЕМ…)

7

+ Повышение надежности

- Избыточность описания

+ Возможность поддержки кода

+ Эффективность выполнения

- Понижение надежности

+ Удобство кодирования

+ Гибкость

~ Понижение производительности

Статическая Динамическая

Вывод типов

ВЫВОД ТИПОВ

Синтаксическая структура программы позволяет составить систему уравнений относительно типов ее частей, автоматическое решение которой избавляет программиста от необходимости явно указывать типы.

8

Вывод типов

ВЫВОД ТИПОВ: SCALA

class StringArrayFactory {

def create: Array[String] = {

val array: Array[String] = Array[String](“1”, “2”, “3”)

array

}

}

9

Вывод типов

ВЫВОД ТИПОВ: SCALA

class StringArrayFactory {

def create = {

val array = Array(“1”, “2”, “3”)

array

}

}

10

Вывод типов

ВЫВОД ТИПОВ: SCALA

object InferenceTest extends Application {

val x = 1 + 2 * 3 // type of x is Int

val y = x.toString() // type of y is String

def inc(x: Int) = x + 1 // succ returns Int values

}

11

Вывод типов

ВЫВОД ТИПОВ: HASKELL

correctChecksum :: ByteString -> Int -> Bool

correctChecksum header checksum = checksum == checksum’

where

−− sum of all 512 bytes in the header block,

−− treating each byte as an 8−bit unsigned value

checksum’ = BS.Char8.foldl’ (\x y -> x + ord y) 0 header’

−− treating the 8 bytes of chksum as blank characters.

header’ = BS.concat [BS.take 148 header,

BS.Char8.replicate 8 ’ ’,

BS.drop 156 header]

12

Вывод типов

ВЫВОД ТИПОВ статически типизированные языки способны

производить вывод типов выражений вида:order.getCustomer().getName()

тут из типа order автоматически выводится тип выражения order.getCustomer() и компилятор убеждается, что для этого типа определена операция getName()

Java, аналогично, позволяет в некоторых простых случаях выводить типовые аргументы методов-дженериковList<String> strings = Collections.emptyList();List<String> strings = Collections.<String>emptyList(); 13

Вывод типов

ВЫВОД ТИПОВ: ПРЕИМУЩЕСТВА…

+Повышение надежность+Эффективность выполнения+Удобство кодирования~Возможность поддержки~Немногословность

14

Вывод типов

СОДЕРЖАНИЕ

Функции Функция высшего порядка Замыкание Частичное применение и карринг Функции вместо объектов

15

ФУНКЦИЯ ВЫСШЕГО ПОРЯДКА Функция, принимающая на вход или

возвращающая функцию.

Оператор отображения списка map:map :: ( a->b ) -> [a] -> [b]> map toUpper [”Hello”, ”World”][”HELLO”, ”WORLD”]

Оператор фильтрации списка filter:filter :: ( a -> Bool) -> [a] -> [a]> filter even [1..10][2,4,6,8,10] 16

Функция высшего порядка

ФУНКЦИЯ ВЫСШЕГО ПОРЯДКА SCALAimport math._

// functions as values

val cube = (x: Double) => x * x * x

val cuberoot = (x: Double) => pow(x, 1 / 3d)

// higher order function, as a method

def compose[A,B,C](f: B => C, g: A => B) = (x: A) => f(g(x))

// partially applied functions in Lists

val fun = List(sin _, cos _, cube)

val inv = List(asin _, acos _, cuberoot)

// composing functions from the above Lists

val comp = (fun, inv).zipped map (_ compose _)

// output results of applying the functions

comp foreach {f => println(f(0.5))}17

Функция высшего порядка

ЗАМЫКАНИЕ

Функция, использующая переменные из области видимости, в которой была создана.

Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. 

18

Замыкание

ЗАМЫКАНИЕ: JAVAclass CalculationWindow extends JFrame { private JButton btnSave; ...

public final void calculateInSeparateThread (final URI uri) { // Пример анонимного класса.

new Thread() { void run() { // Имеет доступ к финальным (final) переменным: calculate(uri); // Имеет доступ к приватным членам содержащего класса: btnSave.setEnabled(true); } }.start();

} }

19

Замыкание

ЗАМЫКАНИЕ: SCALA

var factor = 3

val multiplier = (i:Int) => i * factor

val l1 = List(1, 2, 3, 4, 5) map multiplier

factor = 5

val l2 = List(1, 2, 3, 4, 5) map multiplier

println(l1) // List(3, 6, 9, 12, 15)

println(l2) // List(5, 10, 15, 20, 25)

20

Замыкание

ЗАМЫКАНИЕ: SCALA

val belowFirst = (xs : List[Int]) => {

val first = xs(0)

val isBelow = (y : Int) => y < first

 

for (x <- xs; if(isBelow(x))) 

yield x

}

...

belowFirst(List(5, 1, 7, 4, 9, 11, 3))      

// => List( 1, 4, 3 )

21

Замыкание

ЗАМЫКАНИЕ: HASKELL

f x = (\y -> x + y)

Внутренняя(анонимная) функция использует переменную х из контекста внешней функции f

22

Замыкание

ЗАМЫКАНИЕ: HASKELL

t -> t1 -> (t -> t1 -> t2) -> t2pair a b = \fnc -> fnc a b

first p = p (\a b -> a)second p = p (\a b -> b)

some_pair = pair 1 2

> first some_pair 1> second some_pair2 23

Замыкание

ЗАМЫКАНИЕ: JAVASCRIPTfunction pair(a, b) {

return function (fuc) {return fnc(a, b)

}}

function first(p) {return p(function(a, b) {return a})

}function second(p) {

return p(function(a, b) {return b})}

var somePair = pair(1, 2)first(somePair) // 1second(somePair) // 2 24

Замыкание

ЧАСТИЧНОЕ ПРИМЕНЕНИЕ, КАРРИНГ

Фиксацией некоторых аргументов из функции получается новая функция с меньшим числом аргументов.

У термина «карринг» есть три взаимосвязанных значения1. Фиксация несколько первых аргументов2. Каррирование f: X*Y -> Z,

есть построение f`: X -> (Y -> Z) 3. Применение каррированной функции к

аргументамg = f`(x) 25

Currying

КАРРИРОВАНИЕ

1. частный случай частичного применения, при котором фиксируется несколько первых аргументов функции

add :: Integer -> Integer -> Integer

add x y = x + y

inc :: Integer -> Integer

inc = add 1

26

Currying

КАРРИРОВАНИЕ

2. Превращение функции F над 2-местным кортежем (парой с типами компонентов X и Y ) в функцию над X, возвращающую функцию над Y (такая функция называется «каррированной» версией F)

matchesRegexpUncurried :: (String,String) -> Bool

matchesRegexpUncurried = ...

matchesRegexpCurried :: String -> (String -> Bool)

matchesRegexpCurried pattern = matcher

where matcher s = matchesRegexpUncurried (pattern,s)27

Currying

КАРРИРОВАНИЕ

3. Применение каррированной функции к аргументам:

isNumber = matchesRegexpCurried ”−?[0−9]+”

В таком случае говорят, что процедура isNumber получена каррированием процедуры matchesRegexpCurried

28

Currying

КАРРИРОВАНИЕ SCALA

def cat(s1: String)(s2: String) = s1 + s2

def cat(s1: String) = (s2: String) => s1 + s2

def curry(x:Int)(y:Int) = x + y // curry: (Int)(Int)Int

curry(4)(5) // Int = 9

def multiplier(i: Int)(factor: Int) = i * factor

val byFive = multiplier(5) _

val byTen = multiplier(10) _

29

Currying

КАРРИРОВАНИЕ SCALA

def fs(f:Int=>Int, s:List[Int])=s map f

def f1(x:Int)=x*2

def f2(x:Int)=x*x 

def fsf1 = fs (f1,_:List[Int])

def fsf2 = fs (f2,_:List[Int])

println(fsf1(List(0,1,2,3)))

println(fsf1(List(2,4,6,8)))

println(fsf2(List(0,1,2,3)))

println(fsf2(List(2,4,6,8)))

30

Currying

TRY-WITH-RESOURCES STATEMENT

try (BufferedReader br =

new BufferedReader(new FileReader(path))) { return br.readLine();

}

Any object that implements java.lang.AutoCloseable void close() throws Exception

31

Functions Ending

TRY-WITH-RESOURCES STATEMENT

def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B =

try {f(param)

} finally {param.close()

}

... usage ...using(new BufferedReader(otherReader)) {reader =>

reader.readLine()} 32

Functions Ending

ФУНКЦИИ ВМЕСТО ОБЪЕКТОВ

Замыкание и Каррирование связывают данные и функции аналогично объектам.

33

Functions Ending

СОДЕРЖАНИЕ Введение Вывод типов Функции

Функция высшего порядка Замыкание Частичное применение и карринг Функции вместо объектов

Algebraic data type Сопоставление с образцом Ленивые Вычисления Неизменяемое состояние Benefits 34

ALGEBRAIC DATATYPE

Тип данных, состоящий из нескольких различимых разновидностей (возможно, составных) термов (значений).

Алгебраические типы позволяют определять: типы произведения (кортежи) типы-суммы. «сумма произведений»

35

Algebraic datatype

ALGEBRAIC DATATYPE Типы произведения (кортежи)

data FileInfo where FileInfo { name::String, accessRights::Int, lastModified::Date } :: FileInfo

Типы – суммы

data Color where Red :: Color Orange :: Color Yellow :: Color

36

Algebraic datatype

ПРИМЕР С ДЕРЕВОМ (С++)

37

struct Tree { union { int value; struct { struct Tree *left; struct Tree *right; } branches; } data;

enum { LEAF, NODE } selector;};

data Tree = Leaf Int

∣ Node Tree Tree

Или полиморфное определение

data Tree a = Leaf a

∣ Node (Tree a) (Tree a)

С++ Haskell

Algebraic datatype

ПРИМЕР BILLINGINFO (JAVA)

38

public class BillingInfo {

public static final BillingInfo CreditCard (String number, String holder, String[] address) {

return new CreditCard(number, holder, address); }

public static final BillingInfo CashOnDelivery () {

return new CashOnDelivery(); }

public static final BillingInfo Invoice(String customerId) {

return new Invoice(customerId); }

public static class CreditCard extends BillingInfo {

...

public CreditCard(String number, String holder, String[] address) { ... }

}

public static class CashOnDelivery extends BillingInfo { }

public static class Invoice extends BillingInfo {

...

public Invoice(String customerId) { ... }

}

}

Java

Algebraic datatype

ПРИМЕР BILLINGINFO (SCALA & HASKELL)

39

abstract class BillingInfo

case class CreditCard( val number: String, val holder: String, val address: Array[String]) extends BillingInfo

case class CashOnDelivery extends BillingInfo

case class Invoice( val customerId: String) extends BillingInfo

type Number = String

type Holder = String

type CustomerID = String

type Address = [String]

data BillingInfo =

CreditCard Number Holder Address

| CashOnDelivery

| Invoice CustomerID

Scala Haskell

Algebraic datatype

ALGEBRAIC DATATYPE

Конструкторы алгебраического типа T могут упоминать не только те типовые переменные, по которым параметризован тип T.

Типы результатов веток в определении параметризованного алгебраического типа могут различаться.

data Hash k v where Hash {

hash::k->h,

equal::k->k->Bool,

table::Array h [(k,v)]}40

Algebraic datatype

СОПОСТАВЛЕНИЕ С ОБРАЗЦОМ

41

Сопоставление с образцом

GOOGLE TRANSLATE API

GET https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&source=en&target=de&q=Hello%20world

JSON

{

"data": {

"translations": [

{ "translatedText": "Hallo Welt" }

]

}

}

Как я буду разбирать “это” в Java: 42

Сопоставление с образцом

GOOGLE TRANSLATE API (JAVA)protected void processTranslation (JSONObject json) {

Map data = (Map) json.get("data");

if (data == null) {

return null;

}

List translations = (List) data.get("translations");

if (translations == null || translations.size() == 0) {

return null;

}

Map o = (Map) translations.get(0);

if (o == null) {

return null;

}

String text = (String) o.get("translatedText");

. . . processing . . .

}

43

Сопоставление с образцом

КАК Я БЫ ХОТЕЛprotected void processTranslation (JSONObject match {

"data": {

"translations": [

{ "translatedText": text }

]

}

}) {

. . . processing . . .

}

44

Сопоставление с образцом

СОПОСТАВЛЕНИЕ С ОБРАЗЦОМ

Сопоставление формы структуры данных с формой образца и заполнение переменных-«дырок» в образце значениями в соответствующих местах структуры данных.

45

Сопоставление с образцом

СОПОСТАВЛЕНИЕ С ОБРАЗЦОМ В HASKELL

zip xs [] = []

zip [] xs = []

zip (x:xs) (y:ys) = (x,y):zip xs ys

> zip [1,2,3] [4,5,6]

[(1,4),(2,5),(3,6)]

46

Сопоставление с образцом

КОНЕЧНЫЙ АВТОМАТ ДЛЯ ПОДСЧЕТА СЛОВ

countWords s = space 0 s where space n [] = n space n (’ ’:rest) = space n rest space n (c:rest) = word (n+1) rest word n [] = n word n (’ ’:rest) = space n rest word n (c:rest) = word n rest

47

Сопоставление с образцом

СОПОСТАВЛЕНИЕ С ОБРАЗЦОМ В SCALA

sealed trait Treecase object Empty extends Treecase class Leaf (n: Int) extends Treecase class Node (l: Tree, r: Tree) extends Tree

. . .

def depth(t: Tree): Int = t match { case Empty => 0 case Leaf(n) => 1 case Node(l, r) => 1 + max(depth(l), depth(r))

} 48

Сопоставление с образцом

ЛЕНИВЫЕ ВЫЧИСЛЕНИЯ

49

Ленивые Вычисления

ОТЛОЖЕННЫЕ (ЛЕНИВЫЕ) ВЫЧИСЛЕНИЯ

Концепция, согласно которой вычисления следует откладывать до тех пор, пока не понадобится их результат.

Ленивые вычисления позволяют сократить общий объём вычислений за счёт тех вычислений, результаты которых не будут использованы.

50

Ленивые Вычисления

МЕХАНИЗМЫ ВЫЗОВА ФУНКЦИИ

x = f(exp) Вызов по значению

Вычисляем exp, подставляем результат в f

Вызов по текстовой заменебез замены переменных для устранения

конфликта имен Вызов по имени

с обходом конфликта имен с помощью переименования, но с повторным вычислением выражения

Вызов по необходимости Вызов по имени + мемоизация

51

Ленивые Вычисления

ЛЕНИВЫЕ ВЫЧИСЛЕНИЯ

Примерами нестрогих вычислений в императивных языках могут быть операции && и ||.

boolean res = findFist() || findSecond()

52

Ленивые Вычисления

БЕСКОНЕЧНЫЕ СТРУКТУРЫ :)

бесконечная последовательность единичек

ones = 1 : ones

numsFrom, которая получает один аргумент — целое число n — и возвращает список всех целых чисел, которые больше либо равны n

numsFrom n = n : numsFrom (n + 1)

Ленивое определение структур fib = 1:1:[ a+b | (a,b) <- zip fib (tail fib)]

53

Ленивые Вычисления

DATA STRUCTURE & FOLDING

Data structure Вместо изменения структуры данных можно

формировать новую структуру, немного отличающуюся от старой.

Folding Вычисление снизу вверх «по индукции»,

применяющее в каждом узле структуры данных оператор, соответствующий данному типу узла, к содержимому узла и результатам для его подузлов.

Структурная рекурсия (индукция) Рекурсия, при которой аргумент рекурсивного

вызова в каком-то смысле строго меньше аргумента исходного.

54

Programming / Practice / Data structure

PROGRAMMING: BENEFITS

Чистота Easy Concurrent Programming

λ-исчисление Формальность Доказуемость

Выразительность (композиция) Скорость разработки

Ленивость … Cкорость выполнения

55

ЧИСТОТА

Язык программирования является чистым в том случае, если все функции в программах этого языка являются чистыми.

Чистая функция: Является детерминированой:

если для одного и того же набора входных значений она возвращает одинаковый результат.

Не обладает побочными эффектами: не может изменить значения глобальных

переменных; не может модифицировать переданные в функцию

параметры

56

Benefits

ВЫРАЗИТЕЛЬНОСТЬ

57

Benefits

LITERATURE http://www.google.com http://ru.wikipedia.org/ http://www.haskell.org http://fprog.ru/ http://habrahabr.ru/blogs/Haskell/ http://www.rsdn.ru/article/haskel http://learnyouahaskell.com

58

ПОЧЕМУ “НИКТО” НЕ ИСПОЛЬЗОВАЛ ФУНКЦИОНАЛЬНЫЕ ЯЗЫКИ

Совместимость с другими языками Поддержка языка (как инструмента) Инструментальные средства Обучение Популярность

Библиотеки Мобильность (переносимость) Возможности упаковки

Эффективность

59

Summary

ГДЕ СЕЙЧАС ИСПОЛЬЗУЕТСЯ ФП?

Mainstream языки программирования:C# 3.0, следующий стандарт C++ Java.next (Clojure, Groovy, JRuby, Scala)LINQXSLT

ПриложенияAutoCADemacs (LISP)HeVeA

60

Summary

PROGRAMMING: IDEAS

61

Recommended