In object-oriented design, dependency injection is a well-known design pattern, although it's a complicated solution to the problem of decoupling. Functional programming offers a simpler way. This talk examines dependency injection in object-oriented design, and explains how it's not required (nor desired) in functional programming. You don't need to know Haskell or F# to attend this session; relevant syntax will be explained just-in-time. Object-oriented examples will be in C#.
46. public class ReservationsController : ApiController
{
public ReservationsController(IValidator validator, IMapper mapper, IMaîtreD maîtreD)
{
this.validator = validator;
this.mapper = mapper;
this.maîtreD = maîtreD;
}
public IHttpActionResult Post(ReservationRequestDto dto)
{
var validationMsg = validator.Validate(dto);
if (validationMsg != "")
return this.BadRequest(validationMsg);
var r = mapper.Map(dto);
var id = maîtreD.TryAccept(r);
if (id == null)
return this.StatusCode(HttpStatusCode.Forbidden);
return this.Ok();
}
}
POST /reservations HTTP/1.1
{
"date": "2016-12-08",
"name": "Mark Seemann",
"email": "mark@ploeh.dk",
"quantity": 4
}
@ploeh
47. public class MaîtreD : IMaîtreD
{
public MaîtreD(int capacity, IReservationsRepository reservationsRepository)
{
this.capacity = capacity;
this.reservationsRepository = reservationsRepository;
}
public int? TryAccept(Reservation reservation)
{
var reservedSeats = reservationsRepository
.ReadReservations(reservation.Date)
.Sum(r => r.Quantity);
if (reservedSeats + reservation.Quantity <= capacity)
{
reservation.IsAccepted = true;
return reservationsRepository.Create(reservation);
}
return null;
}
}
@ploeh
public interface IMaîtreD
{
int? TryAccept(Reservation reservation);
}
48. How do I do
dependency
injection in
Scala?
You don’t,
because Scala
is a functional
language.
Comic courtesy of
@jrecursive and @hmemcpy @ploeh
49. Fine, it’s functional.
How do I inject
dependencies?
You use a Free Monad,
which allows you to
build a Monad from
any Functor.
Comic courtesy of
@jrecursive and @hmemcpy @ploeh
50. Did you just
tell me to go
fuck myself?
I believe I
did, Bob.
Comic courtesy of
@jrecursive and @hmemcpy @ploeh
52. // int -> (DateTimeOffset -> Reservation list) -> (Reservation -> int) -> Reservation
// -> int option
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
@ploeh
Repository
53. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
@ploeh
string -> DateTimeOffset -> Reservation list
54. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
@ploeh
string -> DateTimeOffset -> Reservation list
connectionString
55. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
@ploeh
string -> DateTimeOffset -> Reservation listconnectionString
56. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
@ploeh
DateTimeOffset -> Reservation list
57. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
let create = DB.createReservation connectionString
tryAccept 10 read create reservation
@ploeh
Reservation -> int
58. // Reservation -> int option
let tryAcceptComposition reservation =
let read = DB.readReservations connectionString
let create = DB.createReservation connectionString
tryAccept 10 read create reservation
@ploeh
59. // Reservation -> int option
let tryAcceptComposition =
let read = DB.readReservations connectionString
let create = DB.createReservation connectionString
tryAccept 10 read create
@ploeh
60. // Reservation -> int option
let tryAcceptComposition =
let read = DB.readReservations connectionString
let create = DB.createReservation connectionString
tryAccept 10 read create
@ploeh
78. // int -> (DateTimeOffset -> Reservation list) -> Reservation -> Reservation option
let tryAccept capacity readReservations reservation =
@ploeh
79. // int -> (DateTimeOffset -> Reservation list) -> Reservation -> Reservation option
let tryAccept capacity readReservations reservation =
let reservedSeats =
readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity)
if reservedSeats + reservation.Quantity <= capacity
then { reservation with IsAccepted = true } |> Some
else None
@ploeh
80. // Reservation -> int option
let tryAcceptComposition reservation =
match tryAccept 10 (DB.readReservations connectionString) reservation with
| None -> None
| Some r -> (DB.createReservation connectionString) r |> Some
@ploeh
81. // Reservation -> int option
let tryAcceptComposition reservation =
tryAccept 10 (DB.readReservations connectionString) reservation
|> Option.map (fun r -> DB.createReservation connectionString r)
@ploeh
87. // int -> (DateTimeOffset -> Reservation list) -> Reservation -> Reservation option
let tryAccept capacity readReservations reservation =
@ploeh
88. // int -> ( Reservation list) -> Reservation -> Reservation option
let tryAccept capacity readReservations reservation =
@ploeh
89. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity readReservations reservation =
@ploeh
90. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity radReservations reservation =
@ploeh
91. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity rdReservations reservation =
@ploeh
92. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity rReservations reservation =
@ploeh
93. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
94. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
95. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
96. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
97. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
98. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
99. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
100. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
101. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
102. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
103. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
104. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
105. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
106. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
107. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
108. // int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
@ploeh
109. // int -> Reservation list -> Reservation -> Reservation option
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
@ploeh
110. // int -> Reservation list -> Reservation -> Reservation option
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
@ploeh
111. // ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
let flip f x y = f y x
// Reservation -> int option
let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> fun reservations -> tryAccept 10 reservations reservation
|> Option.map (DB.createReservation connectionString)
@ploeh
112. // ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
let flip f x y = f y x
// Reservation -> int option
let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> fun reservations -> flip (tryAccept 10) reservation reservations
|> Option.map (DB.createReservation connectionString)
@ploeh
113. // ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
let flip f x y = f y x
// Reservation -> int option
let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> -> flip (tryAccept 10) reservation
|> Option.map (DB.createReservation connectionString)
@ploeh
114. // ('a -> 'b -> 'c) -> 'b -> 'a -> 'c
let flip f x y = f y x
// Reservation -> int option
let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> flip (tryAccept 10) reservation
|> Option.map (DB.createReservation connectionString)
@ploeh