Functional programming adherents like to entice beginners with promises of perfect code expressiveness, 100% correctness, ease of support and ease of refactoring, and sometimes they even predict the highest performance. However, experienced developers know that this does not happen. Programming is hard work, and “magic pills” do not exist.
On the other hand, elements of a functional programming style have already penetrated into industrial programming languages such as Swift and Kotlin. The developers of these languages are well acquainted with functional programming, so they were able to use it “in the small”, providing for many, though not all, of the necessary components. The farther - the more parts of FI are introduced into industrial nuclear weapons, and the better and more fully the support is implemented.
It is useful to be able to program in a functional style in order to simplify our work, and now we will see how to use it!
Vitaly Bragilevsky is a teacher of FP, the theory of algorithms and computation, the author of the book “Haskell in Depth” and a member of the Haskell 2020 committees and the supervisory committee of the GHC compiler.
Short story
Functional programming stories are not always true. Often people talk about AF like this.
“Once upon a time in a faraway galaxy, programming was simple, straightforward, and good.Algorithms and data structures are programmed and everything is fine - no problems!
Then came the Sith scary people who decided that all programs consist of classes: '' Object-oriented programming for everyone!Everything needs to be written only by such methods. ''
When they gained strength, it became impossible to develop software - a lot of problems appeared.To save the unfortunate suffering developers, there is functional programming. ”
The main thing in salvation is to draw a beautiful and meaningless picture.
When you apply everything in the picture, happiness, peace and tranquility will come, and the problems of software development will be solved.
The story is beautiful, but in reality everything is different. This is how I represent functional and industrial programming.
Functional programming is the laboratory of Faraday - the birthplace of ideas that are then applied in industrial programming.
In a conversation about industrial programming languages, the concepts of whether they are functional or not, whether they use FP or not, should not be raised!
The main mission of the FP is to convey good elements to mainstream languages.
To want to transfer everyone to functional rails is the same as forcing scientists to conduct experiments like Faraday - it makes no sense. No one is currently conducting experiments on electricity with such obsolete methods.
Let's look at examples.
John McCarthy Function
John McCarthy is one of the creators of Lisp.
Fortran was the main programming language 65 years ago. It has a conditional statement:
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
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 . , , , . , , — , — , , .
Circle
Square
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 ? , , , . , -, .
. , , , , , .
Circle
Square
x
area
*
*
?
diameter
*
*
?
f
?
?
?
, , , - — . . : — . , , .
Circle
Square
x
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
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
{-@ 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
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