Jak poznat, že byste měli zkusit programovat funkcionálně

Článek je psán z pohledu C# vývojáře, který koketuje s F#. Ale může být stejně užitečný i pro Javisty, které bych rád nasměroval ke Scale.

1. Když si myslíte, že volitelná nullovatelnost by měla být zabudovaná do celého typového systému.

V C# od verze 2.0 je Nullable<T> wrapper na structy (hodnotové typy), který umožňuje dát explicitně najevo, že API uvažuje o „neexistujících hodnotách“. Spoustě komplexity s ArgumentNullException a chyb s NullReferenceException by se dalo vyhnout, kdyby classy (referenční typy) také nebyly nulovatelné automaticky a vždy. Přesně takto uvažují funkcionální jazyky. Všechny typy jsou nenulovatelné, a pokud nějaká metoda nemusí vrátit hodnotu, používá Option wrapper. Takto je deklarován třeba v F#:

type Option<‚a> =
    | Some
of ‚a
    | None

2. Když jste si zamilovali fluent API, extension metody a LINQ

Pokud také:

  • přepisujete cykly na sadu volání System.Linq.Enumerable metod.
  • upravujete svoje API, aby místo Foo(Bar(x)); se používalo Bar(x).Foo();
  • na situace: if (!model.Kriteria.Address.Code.HasValue) throw ;
    máte připravenou extension metodu: if(model.Kriteria.Address.Code.HasValue.Not()) throw ;

Všechny tyto úpravy napomáhají čitelnosti, protože přeskládávají kód do přirozeného pořadí – pořadí, v jakém se kód vykonává.

F# toto zjednodušuje pipe operátorem |> který nedělá nic jiného, než že výsledek expression na levé straně pošle jako (poslední) parametr funkce na straně pravé. Navrhnout API, které toho dokáže využít, pak neznamená nic víc, než parametry metod, které přijímají (nejčastěji se měnící) data, řadit vždy na poslední místo.

 

[0..100]

    |> List.filter (fun x -> x % 2 = 0)

    |> List.map (fun x -> x * x)

    |> List.sum

    |> printfn „%i“ //vypíše součet druhých mocnin všech sudých čísel do 100

 

3. Když chcete dekomponovat, ale odrazuje vás množství nutného syntaktického bordelu

A to platí jak pro struktury, tak pro logiku.

Typická situace, kdy potřebujete modelovat nějakou (stromovou) strukturu, ale množství kódové vaty na definici celé hierarchie tříd je neúnosné. Ve funkcionálních jazycích bývá výborná podpora jednoduchých typů (record, tuple) a jejich kombinovaní (union):

type MenuItem =

    | Group of string * MenuItem list

    | Page of string * string

let procFsharp = Page („Proč F#“, „Text článku o F#“)

let oAutorech = Page („O autorech“, „Něco o nás“)

let menu = Group („Články“, [procFsharp;oAutorech])

A i kdybychom vytvořili odpovídající strukturu, budou nám chybět konstrukce jak s ní pohodlně pracovat. Nejlepší možnost je as operátor a kontroly na null. Funkcionální jazyky nabízí pattern matching:

match menuItem with

    | Page (titulek,text) -> printfn „Stránka %s o %i znacích“ titulek text.Length

    | Group (titulek,stranky) -> printfn „Skupina %s s %i stránkami“ titulek stranky.Length

Druhá typická situace, kdy v rámci jedné metody je potřeba opakovat pár-řádkový blok kódu, jsou vcelku běžné a způsoby, jak se s tím vypořádat zpravidla, tři:

  • Vyextrahovat do privátní metody – problém může být, že je přístupná i odjinud ze třídy, že překáží v intellisense a celkově je problém ji v rámci třídy správně umístit (k metodě ve které se používá, nebo mezi další helper metody) i pojmenovat (aby dávala smysl v rámci metody i v rámci třídy).
  • Nechat duplicitní kód – problém nastává ze zřejmých důvodů…
  • Nadeklarovat si lokální delegát a smířit se s ručním psaním jeho ohyzdné signatury, jelikož typová inference v tomto případě nepomůže.

Ve funkcionálních jazycích se doporučuje vytvářet mnoho drobných helper funkcí, které není problém deklarovat jako lokální, a to v jakékoliv úrovni zanoření (třeba uvnitř else větve ifu). Navíc silně typované funkcionální jazyky (jako F# i Scala) mají velmi mocnou typovou inferenci, přesahující co nabízí jejich OO dvojčata.

Func<string, int, int, string, string> formatAddress = (ulice, cp, psc, mesto) => $“{ulice} {cp}, {psc} {mesto}; //C#

let formatAddress ulice cp psc mesto = sprintf „%s %i, %i %s“ ulice cp psc mesto //F# explicitní deklarace

let formatAddress = sprintf „%s %i, %i %s“ //F# point-free style – sám odvodí parametry z volané funkce

formatAddress „Rumjancevova“ 77 46001 „Liberec“ |> printfn „%s“

4. Když používáte ternární operátor častěji než if-else

Obecněji – pokud píšete vše, co se dá raději jako expression, než jako statement. Ve funkcionálních jazycích je totiž vše expression – vše vrací „nějakou hodnotu“. To umožňuje libovolné kombinování všech dostupných konstruktů. Ale především to usnadňuje čitelnost a předvídatelnost kódu – když klidně i dlouhý kus kódu má jasný vstup a výstup, na rozdíl od spousty větvení a vedlejších efektů.

let znamenko x = if x < 0 then -1 else +1 //F# a Scala proto nepotřebuje ternární operátor

5. Když chcete zkusit něco nového a být cool 🙂

Advertisement

Zanechat odpověď

Vyplňte detaily níže nebo klikněte na ikonu pro přihlášení:

Logo WordPress.com

Komentujete pomocí vašeho WordPress.com účtu. Odhlásit /  Změnit )

Twitter picture

Komentujete pomocí vašeho Twitter účtu. Odhlásit /  Změnit )

Facebook photo

Komentujete pomocí vašeho Facebook účtu. Odhlásit /  Změnit )

Připojování k %s