TGViewer
melikhov.dev melikhov.dev @melikhov_dev · 4.74K subscribers
Post #251 5.97K
Приключения DI в мире функционального программирования

Когда-то давным-давно (в 2011 году), Марк Симан написал отличную книгу «Внедрение зависимостей в .NET». Всё, что нужно знать про DI в ООП там есть. Маст рид. А несколько лет спустя Марк ударился в функциональное программирование и написал достаточно резонансную серию небольших статей "From dependency injection to dependency rejection" (а также поездил с докладом).

Сегодня у нас будет TL;DR по этой серии.

Итак, Марк начинает с демонстрации «классического» ООП внедрения зависимостей через конструктор объекта. Классика. Все мы делали это. А дальше Марк задаётся вопросом: а как быть в с ФП? Конструктора у нас нет, а значит все параметры нужно передавать на вход функции. Это неудобно, наш API становится чудовищным.

Первое, что приходит в голову, это частичное применение. Напишем функцию с 4 параметрами, первые 3 из которых это зависимости (код на F#):


let tryAccept capacity readReservations createReservation reservation =
let reservedSeats =
readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity)
if reservedSeats + reservation.Quantity <= capacity
then createReservation { reservation with IsAccepted = true } |> Some
else None



«Приклеим» зависимости


let tryAcceptComposition =
let read = DB.readReservations connectionString
let create = DB.createReservation connectionString
tryAccept 10 read create


На выходе имеем функцию tryAcceptComposition в которую уже внедрены зависимости и остался один входной параметр reservation.

Дальше Марк делает классный трюк, и превращает код из F# в C# средствами .NET (через его промежуточное представление IL). И код на C# выглядит как обычный объект с внедрением в конструкторе!

Решена ли задача? Можно ли сказать, что частичное применение решает задачу внедрения зависимостей в ФП? Марк предлагает решить спор используя Haskell. F# язык хороший, но он позволяет много больше, чем «настоящие» ФП языки. И что же у нас происходит в Хаскеле? Если мы объявим tryAccept чистой функций, то код не скомпилируется. Потому что зависимости несут сайд-эффекты (походы в базу данных).

Т.е. частичное применение решает вопрос внедрения зависимостей, но оно делает код нефункциональным. Чтобы функции остались чистыми, мы должны отказаться от внедрения зависимостей. И тут Марк переходит к последнему шагу. Он переписывает tryAccept так, чтобы на входе были только «чистые» значения:


let tryAccept capacity reservations reservation =
let reservedSeats = reservations |> List.sumBy (fun x -> x.Quantity)
if reservedSeats + reservation.Quantity <= capacity
then { reservation with IsAccepted = true } |> Some
else None


Эта функция не требует внедрения зависимостей, в ней нет сайд-эффетов. Сайд-эффекты поднимаются на уровень функции-композиции, в ней происходит вся «грязь»


let flip f x y = f y x

let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> flip (tryAccept 10) reservation
|> Option.map (DB.createReservation connectionString)


В итоге Марк постулирует, что функциональное программирование должно отказаться от понятия внедрения зависимостей. Чистые функции не могут вызывать нечистые функции. Мы должны оставить наше ядро чистым и поднять всё нечистое до уровня границы с внешним миром (и снова привет тебе, архитектура портов и адаптеров).
ploeh blog From dependency injection to dependency rejection The problem typically solved by dependency injection in object-oriented programming is solved in a completely different way in functional programming.
  • 👍 35
  • 🤔 12
  • ❤ 3
  • 🤮 2
More from @melikhov_dev
  1. Sep 27, 2026https://www.aymannadeem.com/artificial/intelligence,/developer/tools/2026/09/24/plan-mode-…
  2. Sep 26, 2026Блин, ну нравятся мне TUI решения, сил нет. Просто, красиво и понятно (для меня). Продолжа…
  3. Sep 13, 2026Какой он — личный харнес? Вот мы и пришли в точку, когда уже бизнес не устраивает медленна…
  4. Aug 30, 2026Разобрал своего питонячего новостного бота и собрал нового, уже на Гермесе. Ну как собрал…
  5. Aug 24, 2026Вы могли наверное заметить что как-то мало меня стало в последнее время. Не видно на конфе…
  6. Aug 6, 2026Как же хорошо перечитывается сейчас «Трилогия Муравейника» Гибсона (она же Sprawl trilogy)…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →