多くの人は、関数型プログラミングを非常に複雑で「ハイテク」なものと考え、FPコミュニティの代表者は象牙の塔に住む審美哲学者だと考えています。
最近まで、物事のこの見方は真実からそれほど遠くはありませんでした。FPとは、Haskellとカテゴリー理論を意味します。 最近、状況が変わり、F#、Scala、Reactの支援なしではなく、機能開発のパラダイムがWeb開発の勢いを増しています。 パラダイムであるOOPの観点から日常のタスクを解決するのに役立つ関数型プログラミングの「パターン」を見てみましょう。
OOPは、数十年にわたってアプリケーションソフトウェアの開発に広く使用されています。 私たちは皆、SOLIDとGOFに精通しています。 機能的に同等なものは何ですか?..機能! 関数型プログラミングは単に「異なる」ものであり、他のソリューションを提供します。
機能設計の基本原則(デザイン)
ファーストクラスオブジェクトとしての機能
«» ( C++, C#, Java) - . : ,
(apple -> banana)
.
F# , :
let z = 1 let add = x + y // int -> int ->int
« »
,
(apple -> banana)
,
(banana -> cherry)
,
(apple -> cherry)
. , – .
, . -, (use case)
httpRequest -> httpResponse
. , , .
. . control flow , , …
(Composite) «», , .
!=
, .
int
– . .
Customer
– . .
int -> int
– . «» — .
. , .
( «», record type F#)
, . , «» .type Birthday = Person * Date
( «», discriminated union type F#)
type PaymentMethod = | Cash | Cheque of ChequeNumber | Card of CardType * CardNumber
Discriminated union – . . , , . , , .
«» .
Entity Framework , id.
«»
« 12 ».
int -> int
! 0, .
NonZeroInteger -> int
int -> int option
.
. . (Domain Model) - (Business Rules). , , , . .
, ( ). . ?
. , ( ).
let printList anAction aList = for i in aList do anAction i
. C#. , ( ). :
public static int Product(int n) { int product = 1; // for (int i = 1; i <= n; i++) // { product *= i; // } return product; // } public static int Sum(int n) { int sum = 0; // for (int i = 1; i <= n; i++) // { sum += i; } return sum; // }
F# fold:
let product n = let initialValue = 1 let action productSoFar x = productSoFar * x [1..n] |> List.fold action initialValue let sum n = let initialValue = 0 let action sumSoFar x = sumSoFar+x [1..n] |> List.fold action initialValue
, , C#
Aggregate
, ! , LINQ :)
C#. «»SelectMany
.
interface IBunchOfStuff { int DoSomething(int x); string DoSomethingElse(int x); // - void DoAThirdThing(string x); // }
SRP ISP .
interface IBunchOfStuff { int DoSomething(int x); }
int -> int
. F# , , « » . «» :
let DoSomethingWithStuff strategy x = strategy x
«»
let isEvenWithLogging = log >> isEven >> log // int -> bool
. .
, . : . : .
,
int -> int -> int
. ,
int
,
int
,
int -> int
. n, n — . .
, . , 1920- , — , .
, , .
let three = 1 + 2 let three = (+) 1 2 let three = ((+) 1) 2 let add1 = (+) 1 let three = add1 2
. (Dependency Injection)
// let getCustomerFromDatabase connection (customerId:CustomerId) = from connection select customer where customerId = customerId // let getCustomer1 = getCustomerFromDatabase myConnection
(continuations)
, , . . , , ? « »
int Divide(int top, int bottom) { if (bottom == 0) { // , ? throw new InvalidOperationException("div by 0"); } else { return top/bottom; } }
, , :
void Divide(int top, int bottom, Action ifZero, Action<int> ifSuccess) { if (bottom == 0) { ifZero(); } else { ifSuccess( top/bottom ); } }
- , « » (Pyramid Of Doom)
. :
let ifSomeDo f opt = if opt.IsSome then f opt.Value else None
,
let example input = doSomething input |> ifSomeDo doSomethingElse |> ifSomeDo doAThirdThing |> ifSomeDo (fun z -> Some z)
– «» . , - , . — - «» — , . « ». .
«», . , «» .
:(
bind
let bind nextFunction optionInput = match optionInput with // | Some s -> nextFunction s // None | None -> None
bind
// let example input = let x = doSomething input if x.IsSome then let y = doSomethingElse (x.Value) if y.IsSome then let z = doAThirdThing (y.Value) if z.IsSome then let result = z.Value Some result else None else None else None // let bind f opt = match opt with | Some v -> f v | None -> None let example input = doSomething input |> bind doSomethingElse |> bind doAThirdThing |> bind (fun z -> Some z)
, «monadic bind». , , , «monadic bind» :)
Bind ( JS )
Bind
,Either
,
C#. : . . , ?
string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); validateRequest(request); canonicalizeEmail(request); db.updateDbFromRequest(request); smtpServer.sendEmail(request.Email) return "OK"; }
, . .
string UpdateCustomerWithErrorHandling() { var request = receiveRequest(); var isValidated = validateRequest(request); if (!isValidated) { return "Request is not valid" } canonicalizeEmail(request); try { var result = db.updateDbFromRequest(request); if (!result) { return "Customer record not found" } } catch { return "DB error: Customer record not updated" } if (!smtpServer.sendEmail(request.Email)) { log.Error "Customer email not sent" } return "OK"; }
18 . 200% . , .
bind
. , F#:
:
.
. «, »
, . .
, ,
- 1 + 2 = 3
- 1 + (2 + 3) = (1 + 2) + 3
- 1 + 0 = 1
0 + 1 = 1
- 2 * 3 = 6
- 2 * (3 * 4) = (2 * 3) * 4
- 1 * 2 = 2
2 * 1 = 2
?
- , , . — ().
- ().
- , , ( ).
. .
1 * 2 * 3 * 4 [ 1; 2; 3; 4 ] |> List.reduce (*)
« », «» . 2
1 + 2 + 3 + 4
.
1 + 2
,
3 + 4
— , . — .
reduce
: ? , ? , .
, . , , .
Map / Reduce
— , . Google — .
— «» ( ). , , .
, Event Sourcing — . Flux unidirectional data flow, .
VS
, , — , — .
, — GOF . «» — .