
多くの人は、関数型プログラミングを非常に複雑で「ハイテク」なものと考え、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 . «» — .