of {$slidecount} ½ {$title}, {$author}

Функциональный дизайн и паттерны ФП

Алексей Пирогов
Hexlet

Функциональное программирование кратко

Функции–значения

-- Haskell
map    :: (a -> b)    -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]

(.)    :: (a -> b) -> (b -> c) -> (a -> c)

flip   :: (a -> b -> c) -> (b -> a -> c)

Каррирование

-- Haskell
add     :: (Int, Int)    -> Int
div     :: (Int, Int)    -> Int

add (12, 30) -- 42

curry   :: ((a, b) -> c) -> (a -> b -> c)
uncurry :: (a -> b -> c) -> ((a, b) -> c)

curry add :: Int -> Int -> Int

Частичное применение

;; Clojure
(def add5 (partial + 5))

(add5 10) ;; 15
-- Haskell
add5 = curry add 5
add5 = (+ 5)

twoDividedBy = flip div 2
twoDividedBy = (2 /)

Соединяем функции

circle :: Float             -> Shape
filled :: Color    -> Shape -> Image
scaled :: Float    -> Image -> Image
moved  :: Position -> Image -> Image

($) :: (a -> b) -> a -> b

moved TopLeftCorner  -- moved TopLeftCorner
  $ scaled 2.0       --   ( scaled 2.0
    $ filled Red     --     ( filled Red
      $ circle 200   --       ( circle 200 ) ) )

Соединяем функции

-- Elm
circle 200
  |> fill Red
  |> scale 2.0
  |> move TopLeftCorner
;; Clojure
(->> (circle 200)
  (fill :red)
  (scale 2.0)
  (move :top-left-corner))

Композиция функций

-- Haskell
f x = g  (h  (k x))
f x = g $ h $ k x
f   = g . h . k
;; Clojure
(defn f [x]         (g (h (k x))))
(def  f     (compose g  h  k))

Композиция функций нескольких аргументов

-- Haskell
f x y = g  (h x  (k y))
f x   = g . h x . k
;; Clojure
(defn f [x y]         (g         (h x (k y))))
(defn f [x]   (compose g (partial h x) k))

Паттерны, наконец

Паттерны, наконец

"bracket"

"bracket" — работа с ресурсами

"bracket"

-- Haskell
bracket
  :: IO a        -- выделение ресурса
  -> (a -> IO b) -- освобождение ресурса
  -> (a -> IO c) -- потребление ресурса

withDB conn =
  bracket (connectDB conn) closeDB

withLogger level =
  bracket (initLogger level) closeLogger

"bracket"

-- Haskell
data Env = Env
  { db     :: DB
  , logger :: Logger }

run :: Env -> IO ()

main =
  withDB "dev" $ \db ->
    withLogger Debug $ \logger ->
      run Env{ db = db, logger = logger }

Reader

Reader, также известный как Окружение

Reader

-- Haskell
prepareData :: Settings -> Input  -> Data
calculate   :: Settings -> Data   -> Result
plotResult  :: Settings -> Result -> Image

Reader

-- Haskell
plotInput :: Settings -> Input -> Image
plotInput = runReader
  $ plotResult <=< calculate <=< prepareData

State

State, или изменяемое состояние

State

-- Haskell
items   :: CharId -> Game -> ([Item], Game)
popItem :: Item   -> Game -> ((),     Game)
heal    :: Int    -> Game -> ((),     Game)

State

-- Haskell
tryToHeal :: Game -> (Bool, Game)
tryToHeal = runState $ do
  xs <- items
  case filter isPotion xs of
    (potion: _) -> do
      popItem potion
      heal (getHP potion)
      return True
    _           ->
      return False

Middleware

Middleware — оборачиваем функции в функции

Middleware

type Application
  =  Request
  -> (Response -> IO ResponseReceived)
  -> IO ResponseReceived

type Middleware
  = Application -> Application

Middleware

app :: Application
app req respond =
  respond $ responseLBS status200 [] "Hello!"

withLogging :: Middleware
withLogging app req respond = do
  -- log request
  app req $ \response -> do
    -- log response
    respond response

А ещё у нас есть

Конец

Всем спасибо!