不要骗我函数式编程

函数式编程的拥护者喜欢以完美的代码表示能力,100%的正确性,易于支持和易于重构的方式吸引新手,甚至可以预测最高的性能。 但是,经验丰富的开发人员知道这不会发生。 编程是艰苦的工作,“魔药”不存在。

另一方面,函数式编程风格的元素已经渗透到工业编程语言中,例如Swift和Kotlin。 这些语言的开发人员对函数式编程非常熟悉,因此他们能够“少量”使用它,从而提供了许多(尽管不是全部)必要的组件。 距离越远,将FI的更多部分引入工业核武器中,支持的实施就越充分和充分。

能够以功能样式进行编程有助于简化您的工作,现在我们将了解如何使用它!


Vitaly Bragilevsky是FP的老师,算法和计算理论的作者,《深度的哈斯克尔》一书的作者,并且是Haskell 2020委员会和GHC编译器的监督委员会的成员。

历史简史


函数式编程的故事并不总是正确的。 人们经常这样谈论AF。

“从前在遥远的银河系中,编程是简单,直接和良好的。 对算法和数据结构进行了编程,一切都很好-没问题!

然后是西斯恐怖的人,他们决定所有程序都由类组成:''每个人的面向对象编程! 一切都只需要用这种方法来编写。

当他们获得实力时,就不可能开发软件了-出现了很多问题。 为挽救不幸的苦难开发人员,并提供了功能编程。”

得救的主要目的是画出一幅美丽而毫无意义的图画。



当您应用图片中的所有内容时,幸福,和平与安宁就会到来,并且软件开发的问题将得到解决。

这个故事很美,但实际上一切都不同。 这就是我表示功能和工业编程的方式。



函数式编程是法拉第的实验室-法拉第的思想发源地,这些思想随后被应用到工业编程中。

在关于工业编程语言的对话中,不应提出它们是否起作用,是否使用FP的概念!

FP的主要任务是向主流语言传达良好的元素。

想要将所有人转移到功能轨道上,就像强迫科学家进行法拉第这样的实验一样,这没有任何意义。 目前没有人使用这种过时的方法进行电力实验。

让我们看一些例子。

约翰·麦卡锡(John McCarthy Function)


约翰·麦卡锡(John McCarthy)是Lisp的创建者之一。



65年前,Fortran是主要的编程语言。 它有一个条件语句:

        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/zh-CN462505/


All Articles