Jangan membodohi saya dengan pemrograman fungsional Anda

Penganut pemrograman fungsional suka membujuk pendatang baru dengan janji-janji ekspresi kode sempurna, kebenaran 100%, kemudahan dukungan dan kemudahan refactoring, dan kadang-kadang bahkan memprediksi kinerja tertinggi. Namun, pengembang yang berpengalaman tahu bahwa ini tidak terjadi. Pemrograman adalah kerja keras, dan "pil ajaib" tidak ada.

Di sisi lain, elemen gaya pemrograman fungsional telah merambah ke bahasa pemrograman industri seperti Swift dan Kotlin. Pengembang bahasa-bahasa ini sangat mengenal pemrograman fungsional, sehingga mereka dapat menggunakannya “dalam ukuran kecil”, menyediakan banyak komponen yang diperlukan, meskipun tidak semua. Semakin jauh - semakin banyak bagian FI diperkenalkan ke dalam senjata nuklir industri, dan semakin baik dan semakin penuh dukungan diterapkan.

Mampu memprogram dengan gaya fungsional berguna untuk menyederhanakan pekerjaan Anda, dan sekarang kita akan melihat bagaimana menggunakannya!


Vitaliy Bragilevsky adalah guru FP, teori algoritma dan perhitungan, penulis buku "Haskell in Depth" dan anggota komite Haskell 2020 dan komite pengawas dari kompiler GHC.

Sejarah singkat


Cerita pemrograman fungsional tidak selalu benar. Seringkali orang berbicara tentang AF seperti ini.

“Dahulu kala di galaksi yang sangat jauh, pemrograman itu sederhana, langsung, dan bagus. Algoritma dan struktur data diprogram dan semuanya baik-baik saja - tidak ada masalah!

Lalu datanglah orang-orang menakutkan Sith yang memutuskan bahwa semua program terdiri dari kelas: '' Pemrograman berorientasi objek untuk semua orang! Semuanya perlu ditulis hanya dengan metode seperti itu. ''

Ketika mereka memperoleh kekuatan, menjadi tidak mungkin untuk mengembangkan perangkat lunak - banyak masalah muncul. Untuk menyelamatkan pengembang yang tidak beruntung, dan ada pemrograman fungsional. "

Hal utama dalam keselamatan adalah menggambar gambar yang indah dan tidak berarti.



Ketika Anda menerapkan segala sesuatu dalam gambar, kebahagiaan, kedamaian dan ketenangan akan datang, dan masalah pengembangan perangkat lunak akan terpecahkan.

Ceritanya indah, tetapi kenyataannya semuanya berbeda. Ini adalah bagaimana saya mewakili pemrograman fungsional dan industri.



Pemrograman fungsional adalah laboratorium Faraday - tempat lahirnya gagasan yang kemudian diterapkan dalam pemrograman industri.

Dalam percakapan tentang bahasa pemrograman industri, konsep apakah mereka fungsional atau tidak, apakah mereka menggunakan FP atau tidak, seharusnya tidak dimunculkan!

Misi utama FP adalah untuk menyampaikan elemen-elemen yang baik ke bahasa umum.

Ingin mentransfer semua orang ke pagar fungsional sama dengan memaksa para ilmuwan untuk melakukan eksperimen seperti Faraday - tidak masuk akal. Saat ini tidak ada yang melakukan percobaan pada listrik dengan metode usang.

Mari kita lihat contohnya.

Fungsi John McCarthy


John McCarthy adalah salah satu pencipta Lisp.



Fortran adalah bahasa pemrograman utama 65 tahun yang lalu. Ini memiliki pernyataan kondisional:

        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 f — f;
  • map g — g;
  • 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/id462505/


All Articles