Não me engane com sua programação funcional

Os adeptos da programação funcional gostam de atrair os novatos com promessas de perfeita expressividade do código, 100% de correção, facilidade de suporte e facilidade de refatoração e, às vezes, até prever o melhor desempenho. No entanto, desenvolvedores experientes sabem que isso não acontece. Programar é um trabalho árduo e não existem "pílulas mágicas".

Por outro lado, elementos de um estilo de programação funcional já penetraram em linguagens de programação industrial como Swift e Kotlin. Os desenvolvedores dessas linguagens estão familiarizados com a programação funcional, de modo que puderam usá-la "no pequeno", fornecendo muitos, embora não todos, os componentes necessários. Quanto mais longe - mais partes da FI são introduzidas nas armas nucleares industriais, e melhor e mais plenamente o apoio é implementado.

Ser capaz de programar em um estilo funcional é útil para simplificar o seu trabalho, e agora veremos como usá-lo!


Vitaly Bragilevsky é professor de FP, teoria dos algoritmos e computação, autor do livro "Haskell in Depth" e membro dos comitês Haskell 2020 e do comitê de supervisão do compilador GHC.

Breve histórico


Histórias de programação funcional nem sempre são verdadeiras. Muitas vezes, as pessoas falam sobre AF assim.

“Era uma vez em uma galáxia distante, a programação era simples, direta e boa. Algoritmos e estruturas de dados são programados e está tudo bem - sem problemas!

Depois vieram as pessoas assustadoras dos Sith que decidiram que todos os programas consistiam em aulas: '' Programação orientada a objetos para todos! Tudo precisa ser escrito apenas por esses métodos ''.

Quando eles ganharam força, tornou-se impossível desenvolver software - muitos problemas surgiram. Para salvar os infelizes desenvolvedores que sofrem, e há programação funcional ".

O principal na salvação é desenhar uma imagem bonita e sem sentido.



Quando você aplica tudo na imagem, a felicidade, a paz e a tranquilidade surgirão e os problemas do desenvolvimento de software serão resolvidos.

A história é linda, mas na realidade tudo é diferente. É assim que eu represento a programação funcional e industrial.



A programação funcional é o laboratório de Faraday - o berço das idéias que são então aplicadas na programação industrial.

Em uma conversa sobre linguagens de programação industrial, os conceitos sobre se são funcionais ou não, se usam FP ou não, não devem ser levantados!

A principal missão do PF é transmitir bons elementos para os principais idiomas.

Querer transferir todos para trilhos funcionais é o mesmo que forçar os cientistas a realizar experimentos como Faraday - não faz sentido. Atualmente, ninguém está conduzindo experimentos com eletricidade com esses métodos obsoletos.

Vejamos exemplos.

Função John McCarthy


John McCarthy é um dos criadores do Lisp.



Fortran foi a principal linguagem de programação 65 anos atrás. Tem uma declaração condicional:

        IF (I.NE.0) GOTO 40
        STMT-1
        STMT-2
40      STMT-3

I 0, «40». 

, , . , XIF(M, N1,N2), , : N1 N2.

: «… » , .

— N1 N2. , ?

:

M==0 ? N1 : N2

. . , , - .

?


— . 1977 . « ?». — — , .



— , .

- :

c := 0 
for i := 1 step 1 until n do
    c := c + a[i]*b[i]

— .

. FP.

Def DotP = (Insert +) o (ApplyToAll x) o Transpose

o, , , .

— — , , .

. .


, - Java . , , , .



, , :

  • , ;
  • ;
  • ;
  • .

, .


:

fun countLinesInFiles(fnames: List<String>): Int
    = fnames.map { File(it).readLines().size }
        .sum()

Kotlin, , . ?

, , . — «map» — . , : , . it — . — sum().

, map Kotlin, , . , . — readLines .

Haskell


, Haskell.

countLines :: FilePath -> IO Int
countLines = fmap (length . lines) . readFile 

countLinesInFiles :: [FilePath] -> IO Int
countLinesInFiles = fmap sum . traverse countLines

, , . Haskell , , .

. , : , Int — . 

. «IO», Haskell , -. « », Int, IO Int. , .

. — Haskell. — FilePath, IO Int — . : , traverse — , «countLines».

. «. » ( o) — . , .

«f. g» — , «x» «f(g(x))». , . , . .


.

, , . — , .

UML-. : , , , , .



Swift


, , - . Swift , , .

protocol Shape {
    func area() -> Double
    func diameter() -> Double
}

class Circle: Shape {
    var r = 0.0
    init(radius: Double) {
        r = radius
    }
    func area() -> Double {
        return 3.14 * r * r
    }
    func diameter() -> Double {
        return 2 * r
    }
}

class Square: Shape {
    var s = 0.0
    init(size: Double) {
        s = size
    }
    func area() -> Double {
        return s * s
    }
    func diameter() -> Double {
        return sqrt(2) * s
    }
}

, .

: , , – , . .

, , .

func testShape(shape: Shape) {
    print(shape.area())
    print(shape.diameter())
}

. .

testShape(shape: Circle(radius: 5))
testShape(shape: Square(size: 5))

78.5
10.0
25.0
7.0710678118654755

, , , , - . – , .

Haskell


Haskell, , Swift.

data Shape = Circle Double | Square Double

area :: Shape -> Double
area (Circle r) = 3.14 * r * r
area (Square s) = s * s

diameter :: Shape -> Double
diameter (Circle r) = 2* r
diameter (Square s) = sqrt 2 * s

. , « » — . «» . .

— , , . Haskell — . , .

:

> area (Circle 5)
78.5
> diameter (Circle 5)
10.0
> area (Square 5)
25.0
 diameter (Square 5)
7.0710678118654755

— — , .

, Swift Haskell . , , , . , , — , — , , .
CircleSquare
area**
diameter**
— , — . — , .

, : ? . .

, :

  • — ;
  • — , , , .

— .


, Swift.

Haskell…

data Shape = Circle Double | Square Double

… Swift .

enum Shape { 
    case Circle(radius: Double) 
    case Square(size: Double)
}

. «» , Swift «enum», . , .

Haskell…

area :: Shape -> Double 
    area (Circle r) = 3.14 * r * r 
    area (Square s) = s * s

… Swift .

extension Shape {
    var area: Double {
        switch self {
            case .Circle(let r): return 3.14 * r * r 
            case .Square(let s): return s * s
        }
    }
}

, — .

Kotlin:

sealed class Shape {
    data class Circle(val radius: Double): Shape() 
    data class Square(val size: Double): Shape()
}
fun area(sh: Shape): Double {
    return when (sh) {
        is Shape.Circle -> 3.14 * sh.radius * sh.radius 
        is Shape.Square -> sh.size * sh.size 
    }
}

, — .

, Kotlin Swift ? , , , . , -, .


. , , , , , .
CircleSquarex
area**?
diameter**?
f???

, , , - — . . : — . , , .
CircleSquarex
area**+ ()
diameter**+ ()
f+ ()+ ()?

. .

. . — - , — , . — .


— — . 20 , .

  • «Visitor». , , . , . 
  • .
  • — .
  • .
  • .

, « », . . , — , .


, .

, : , , , . , .



Haskell. , , , «» ?

, «» — .

— «Composite». . , .

, Kotlin — .

val a = "Hello " 
val b = "world"
val c = a + b

, : «, ? ». , — - .

— HTTP-. Kotlin :

val a = HttpReq("f","v1") 
val b = HttpReq("f2","v2")
val c = a + b

Haskell , . HTTP- — . HTTP-. 

. . - . — , — , . «optional chaining», , Swift.

if let johnsStreet = john.residence?.address?.street
{ /* SUCCESS */ } else { /* FAILURE */ }

, . «john» — .

  • ?. , , false. 
  • , .
  • .
  • — .

: , , . — . , else — . .

Swift . , , , : .

Haskell, .

case residence john >>= address >>= street of
    Just johnsStreet -> -- SUCCESS
    Nothing -> -- FAILURE

,  «>>=» — . .

Swift «optional chaining» . Kotlin . Haskell , «>>=». 

:

  • , , .
  • , Haskell.

. . , . , — , .

, — .

«» . — , . «», . « » .

— ?


. , — .

, — . Haskell «Build Systems a la Carte» . Distinguished Paper Award International Conference on Functional Programming (ICFP) - 2018 . Build-, .

Build- Build- — . .

data Store i k v

:

  • i — ;
  • k — , , ;
  • v — , , , .

— . — , . . 

. , , . , - -, , .

«Task» — .

newtype Task c k v = Task { run :: forall f. c f => (k -> f v) -> f v }

Haskell, . , — «f», . , .

? «k -> fv» — - . . . Build-.

— :

type Tasks c k v = k -> Maybe (Task c k v) 

, , . «Maybe» — - «optional». , - - , .

Build-:

type Build c i k v = Tasks c k v -> k -> Store i k v
    -> Store i k v

. k, . — . , .

.

  • Make,
  • Shake — Make Haskell,
  • Bazel,
  • Excel.

. — Excel — . Excel — . , - . Make Excel - , .


.

— Haskell . , .

, . - — . , , . , .


, Haskell:

h :: [a] -> Int
h = length . filter p . map g . map f

— . - , :

  • map ff;
  • map gg;
  • filter p — : , , ;
  • length — — .

:

— , , … ! !

, .

Fusion


«fusion», . 

«fusion»? -: :

c := 0
foreach (x : xs)
    if p(g(f(x)))
        then c := c + 1

, (!), (!). , , 2-3 .

, , , . — .

, .

GPU? !


Haskell, :

dotp :: Acc (Vector Float) -> Acc (Vector Float) -> Acc (Scalar Float) 
dotp xs ys = fold (+) 0 (zipWith (*) xs ys)

, Haskell: 2 , , . , Haskell GPU. GPU, : CUDA, Open CL — , . Accelerate Haskell — .

CUDA  100 , , . ! , . , .


— . ? a, b, c D.

f :: A -> B -> C -> D
f a b c = ...

, — . , :

  • f :: A -> B -> C -> D
  • f a :: B -> C -> D
  • f a b :: C -> D
  • f a b c :: D

, . , , - , . — , , .

, , Scala -.

f :: A -> (B , C) -> D
f a p = …
    f :: A -> B -> C -> D
    f a :: (B, C) -> D
    f a p :: D

: « , . , . , — ». .

, .


. Haskell 15 QuickCheck, .

, , :

reverse :: String -> String
reverse = ...

, . , .

> quickCheck (\s -> reverse (reverse s) == s)
    +++ OK, passed 100 tests.

, . , 100, , , .

, , . . Haskell 15 . . , Swift Kotlin .


, ?

abs :: Int -> Int 
abs x = if x < 0 then 0 - x else x

head :: [a] -> a --      ! 
head (x:_) = x 

example :: Int -> Int
example x = if abs x < 0 then head [] else x

, .

head :: [a] -> a --      ! 
head (x:_) = x 

, head , — .

«head», ? , LiquidHaskell . LiquidHaskell — Haskell-.

{-@ abs :: Int -> { n : Int | 0 <= n } @-} 
abs :: Int -> Int 
abs x = if x < 0 then 0 - x else x

{-@ head :: { xs : [a] | 1 <= len xs } -> a @-} 
head :: [a] -> a --      ! 
head (x:_) = x 

{-@ head :: { xs : [a] | 1 <= len xs } -> a @-} 
head :: [a] -> a --      ! 
head (x:_) = x 

, abs , n.

{-@ abs :: Int -> { n : Int | 0 <= n } @-} 
abs :: Int -> Int 
abs x = if x < 0 then 0 - x else x

, .

{-@ head :: { xs : [a] | 1 <= len xs } -> a @-} 
head :: [a] -> a --      ! 
head (x:_) = x 

, , . x x.

{-@ example :: x : Int -> { y : Int | x == y } @-} 
example :: Int -> Int
example x = if abs x < 0 then head [] else x

, . , . , - . - - , , Eiffel, - .

?


-:

type MyAPI = Get '[PlainText] String
    :<|> "conf" :> Get '[JSON] String
    :<|> "year" :> Get '[JSON] Int
    :<|> "like" :> Get '[JSON] Bool
myAPI :: Server MyAPI 
    = root :<|> year :<|> conf :<|> like

Haskell Servant, API . , url , , , JSON. , : String», Int», Bool.

.

root :: Handler String				
root = pure "Welcome to my REST API" 	
conf :: Handler String				
conf = pure "AppsConf"	   		
year :: Handler Int
year = pure 2019 conf
like :: Handler Bool
like = pure True

— , .

, . , , . — .


— . , . .

« Haskell». 2018 . , . Haskell. 600 , .

«Haskell in Depth» . , , . «slbragilevsky» 42%.

Saint AppsConf , Introductory- , . , , . , iOS, Android, - — . , , .

telegram-, .

Source: https://habr.com/ru/post/pt462505/


All Articles