F# a Recordy rubrika: Programování: .Net

8 xxar3s
položil/-a 12.12.2019
 
upravil/-a 12.12.2019

Na uvod vysvetlim co su recordy. Kedze vacsina .NET programatorov pouziva jazyk C# s recordami bezne neprichadzaju do kontaktu. V F# mame okrem tried (ktore funguju rovnako ako triedy v ostatnych .NET jazykoch) aj Recordy.

Record je nieco medzi triedou a strukturou tym co poznaju JS asi najviac pripomina plain object. Je to vlastne taka prepravka na data alebo anemicky objekt. Teda jazyk nam vyslovene nebrani v tom aby record obsahoval aj metody, alebo dokonca vnutorny stav ako u beznycxh tried ale nepouziva sa to. Record ma jednoducho syntax prisposobneu na funkcionalne programovanie. Priklady

// deklaracia typu recordu:
 
type Person =
    {
        FirstName : string
        LastName : string
        Age : byte
    }
 
// inicializacia recordu
 
let pata =
    {
        FirstName = "Pata"
        LastName = "Buckova"
        Age = 22
        Company = "PlutonKrea"
        Profession = "Consultant"
    }

Recordy sa pri funkcionalnom programovani oplati pouzivat preto ze su immutable, podporuju jednoduche klonovanie, alebo update len niektorych hodnot. Zoberiem rekord "pata" a chcem vytvorit novy rekord "karin" s vekom 20 rokov a inym menom:

let karin =
    { 
        pata with
            Age = 20
            FirstName = "Karin"
            LastName = "Ivancikova"
    }

hodnoty Company a Profession ostanu rovnake ako v recorde "pata" pretoze sa povodny record okopiroval.

v novej verzii F# mame okrem toho aj anonymne recordy, ktore nepotrebuju definovat typ recordu ale tym sa teraz nechcem venovat (az na iny zapis funguju uplne rovnako) tak len jeden priklad:

let car =
    {|
        Brand = "Tesla"
        Model = "S"
        Colour = "Red"
    |} 

v com su recordy ine ako trieda:

  • su immutable (da sa samozrejme spravit aj mutable record ale treba k tomu jazyk vyslovene donutit)
  • jednoduchsia syntax zapisu
  • jednoduche klonovanie - klonovanie je pri funkcionalnom programovani dost uzicotna vec
  • membery su public (teoreticky si viem pridat aj private membery ale ide to proti ich filozofii)
  • moze sice obsahovat aj metody ci properties (getter / setter), ale bezne sa tam metody nepridavaju (nato su urcene plnokrvne triedy)
  • narozdiel od struktury je Record referencny typ vytvarany na heape - atributmi sa da donutit aby bol hodnotovy v tom pripade to bude obycajna struktura.
    Samozrejme pri immutable recordoch rozdiel medzi hodnotovym a referencnym typom na prvy pohlad nevidime. Ale ked si spravime mutable record tak hned je jasne ze ide o referencny typ
  • recordy nepodporuju dedicnost ani implementovanie interfacov.
  • v C# sa recordy z jazyka F# tvaria ako bezna anemicka trieda.

A teraz problem:

Problemom je ta dedicnost a nemoznost implementovat interface. V .NET je vsetko postavene na interfacoch. Mam jednu kniznicu, ktora pracuje len s objektami implementujucimi jeden interface (neni dolezite aky). No rad by som na ten ucel pouzil recordy ale bez toho aby record imlemetoval dany interface sa to neda.

Neda sa nejakym atributom povedat .NETu ze trieda implementuje taky a taky interface? (pricom potrebne metody by som tam rucne popridaval?)

odkaz
1 pudr
odpověděl/-a 18.12.2019
 
upravil/-a 18.12.2019

Pokud si pamatuju pak implementovat interface jde:

type IMyInterface =
    abstract GetValue: unit -> string
 
type MyRecord = 
  { MyField1: int
    MyField2: string }
  interface IMyInterface with
    member x.GetValue() = x.MyField2

Komentáře

  • xxar3s : Wow funguje to. Tak o tomto som vobec nevedel diky. 18.12.2019
  • pudr : @xxar3s: BTW že je struktura (hodnotový typ) alokován na zásobníku je mýtus blogs.msdn.microsoft.com/ericlippert/2010/09/30/the-truth-about-value-types 19.12.2019
  • xxar3s : Dik za info pudr. Hej pre managed prostredie su tieto zalezitosti implementacny detail a casto moze vyvojar len hadat, kde to ma skutocne ulozene, aj ked pre vacsinu use cases to vediet nepotrebuje. Ale ked chce mat vyvojar istotu, ze sa objekt vytvori na stacku tak si ho bude musiet alokovat rucne pomocou stackalloc a rovnako to plati aj pre heap (Marshal.AllocHGlobal) kde si moze vyvojar zapisovat co len chce kludne aj tie hodnotove typy. 20.12.2019
  • Taco : @xxar3s: Můžeš uvést nějaký příklad, kdy by vývojář potřeboval mít jistotu, že se to uloží na stacku? 20.12.2019
  • xxar3s : Podla mna to vies aj ty Taco... Stack je mala velmi rychla pamat (pricom velkost alokovaneho miesta sa neda menit). Na stacku sa nemusis starat o uvolnovanie, pretoze sa uvolnuje automaticky po dobehnuti procedury / funkcie. Heap je pomalsia pamat, pri ktorej mozes manipulovat s velkostou alokovaneho miesta. Ale treba si ju potom upratat (pri managed typoch ju uvolnuje GC, ale obcas sa stava ze nam pouzitie GC nevyhovuje a musime použiť unmanaged typy alebo si do pamate zapisujeme priamo cez pointer či Span, alebo UnmanagedMemoryStream, typicky chceme mat kontrolu nad tym kedy bude pamat uvolnena) Takze pri rychle vypocty sa oplati data ukladat na stack (ak dat neni vela, lebo inak nam hrozi pretecenie zasobniku). Ak potrebujeme s datami pracovat este rychlejsie tak ich mozme ukladat rovno do registrov procesora v C++ na to sluzi keyword register, v C# neviem - aj ked kvalitny compiler si tieto veci zoptimalizuje sam. Naopak do heapu treba ukladat velke objemy dat, ktore maju dlhsiu platnost ako call stack procedury / funkcie. A este doplnim ze pri OOP ma stack jedno velke obmedzenie nefunguju v nom virtualne metody. Ale nechcem tu rozdavat rozumy a je kludne mozne ze sa v niecom mylim, tak ak nemam v niecom pravdu, tak ma kludne opravte budem len rad. 20.12.2019
  • Taco : @xxar3s: Jaký je rozdíl mezi stack a heap to vím, včetně jejich omezení a důsledků. Mě zajímala situace, kdy by to vývojáře mělo zajímat. Obecně totiž tlačím názor, že vývojář by do toho neměl pokud možno vůbec kecat páč nadělá víc škody než užitku. Všechno by si měl rozhodnout compiler. Krásným příkladem budiž jazyk go, který díky chytré escape analýze nechává na GC fakt jen to co už nejde. 20.12.2019
  • xxar3s : Mas pravdu, existuju situcie ked kompiler ci behove prostredie optimalizuje lepsie ako clovek a su aj situacie ked ma clovek na vrch. Ale praca s Arrayom vytvorenym na stacku bude isto rychlejsia ako praca s managed array. Niekedy ti len malicka uprava kodu dost zasadne zrychly celu ulohu. Pamatam si ked som ako zacinajuci programator (pred cca 20 rokmi ked som kodil svoj program upravu grafiky) s uzasom zistil ze napriklad len obycajna vymena floatu za integer mi niekolkonasobne zrychlila cely vypocet z niekolko sekund na stovky milisekund. niekedy sa k takejto uprave dopracujes cielene (ked mas vela skusenosti z minulosti lebo vychadzas z teoretickych znalosti) a niekedy metodou pokus / omyl. Preto mame moznost vyberu preto aj tak vysokourovnovy jazyk ako C# umoznuje dostat sa pod poklicku. A prax nam ukazuje ze aplikacie naisane v nizkourovnovych jazykoch, kde sa da optimalizovat a programatori su zvyknuti bezne optimalizovat (ako C++, Rust) su casto omnoho rychlejsie. Ale nerad by som tu teraz otvaral nekonecnu filozoficku debatu "ci bolo prve vajce, alebo sliepka". Howgh. 21.12.2019
  • harrison314 : @xxar3s: Ak chces mat istotu, ze struktura bude iba zasobniku, tak od C# 7.3 mas na to ref struct. A este detail v OOP funguju virtualne metody aj objekty na stacku (pozri prednasku Adam Furmanek - Manual Memory Management), ale ver, ze to robit nechces a je to len taky fun fact (a temna magia). 21.12.2019
  • Kit : @xxar3s: V heapu je pomalejší jen vytváření a rušení pole. Samotná práce s ním je však stejně rychlá jako ve stacku. 21.12.2019

Pro zobrazení všech 5 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.