関数型プログラミングの「パターン」


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

Source: https://habr.com/ru/post/J337880/


All Articles