Dart Typesystem

Ľudia zvyknutí na Java, C#, či C++ ohŕňajú nosom nad tým, že Dart je dynamicky typovaný. Ľudia odchovaní na Pythone, Javascripte či Ruby ohŕňajú nosom nad tým, že Dart to s podporou dynamických features príliš nepreháňa. Ľudia obľubujúci Dart nosom neohŕňajú a Dart-ovský typesystem pokladajú za najlepší vynález hneď po krájanom chlebe. O čom táto kontroverzia vlastne je, a ako to celé funguje?

Seriál: Úvod do Dartu (9 dílů)

  1. Dart – Čo? Prečo? 2.8.2013
  2. Dart – Úvod do jazyka 23.8.2013
  3. Dart – Ponorme sa hlbšie 6.9.2013
  4. Dart – v DOMe 19.9.2013
  5. Dart – Futures 4.10.2013
  6. Dart ­– Streams 17.10.2013
  7. Dart – Používame JavaScript 1.11.2013
  8. Dart Typesystem 19.11.2013
  9. Dart – Neznesiteľná ľahkosť asynchrónneho bytia 2.12.2013

Dart o sebe na oficiálnej stránke tvrdí, že je dynamicky typovaný. Toto tvrdenie hneď aj dokladá ukážkou, ktorej parafrázu si ukážeme:

  var i = 10;
  i = new Object();
  i = "dart IS dynamically typed language";
  print(i.length); // vypise 34

Ukážka sa skompiluje, spustí a korektne vypíše výsledok, úplne ignorujúc problém (problém?), že premenná i menila typ (číslo, objekt, string), ako sa nám zachcelo. Ak sa „spýtame“ editora, čo si myslí o type premennej i , (podržíme nad ňou myš), dozvieme sa, že je typu dynamic (čítaj: hocičo). Keby sme do kódu pridali riadok print(i.runtimeType); dozvedáme sa, že i je String.

 Trochu poriadku do toho dynamična

Zameňme v ukážke deklaráciu var i = 10; za num i = 10; . Pre úplnosť: num je spoločným predkom tried int a double. Nastanú dve veci – editor nám ukáže warning a pokiaľ ho budeme ignorovať (jasné, že budeme), program havaruje s hláškou:

"type 'Object' is not a subtype of type 'num' of 'i'."

Na prvý pohľad to môže pôsobiť tak, že pridaním informácie o type premennej i sme zapli akýsi iný – typovo orientovaný – Dart. To však nie je pravda, dokonca, je to úplne MYLNÁ predstava. Dart totiž zo svojej podstaty ignoruje informácie o type. Tie slúžia (doslova) len na okrasu (teda, pomáhajú ľuďom a tiež DartEditoru rozumieť kódu) a na samotný beh kódu nemajú vplyv.

Počujem nesúhlasné hvízdanie? Ak Dart ignoruje typy, tak prečo náš program havaruje s hore uvedenou výnimkou? DartEditor defaultne púšťa kód v tzv. checked móde. V praxi si to môžme predstaviť tak, že za každým priradením sa prevedie kontrola, či premenná má správny typ, ak nie, vyhodí sa výnimka. Schválne, skúste checked mód vypnúť (v run/manage launches) a všetko pobeží hladko ako predtým. Checked mód je zamýšľaný ako pomôcka pre vývoj, že to je fakt dobrá pomôcka mi asi uverí každý, kto skúsil v JavaScripte napísať viac ako 4 riadky kódu.

Pred filozofickým zamyslením na záver ešte dve zastaveniahodné záležitosti.

Typ pre funkciu

Majme funkciu, ktorá ako svoj parameter akceptuje inú funkciu (toto sa v Darte a tiež kope iných kultúrnych jazykov dá spraviť). Vezmime napríklad takýto kus kódu:

num feedAll(List<Animal> animals, dynamic feedOne){
  num consumed = 0;
  for(Animal animal in animals){
    consumed += feedOne(animal);
  }
  print("${consumed} food was consumed");
}

//neskor volame feedAll 

feedAll([alfie, balthazar, carly], (animal){
  num foodConsumed = animal.feed();
  print("feeding ${animal.name}. NOM NOM NOM");
  return foodConsumed;
});

Funkcia feedAll akceptuje ako argument feedOne, ktorého typ sme zatiaľ kvôli nedostatku kreativity špecifikovali ako dynamic. V skutočnosti však vieme, že feedOne má byť funkcia, ktorá akceptuje argument typu Animal a vráti num. Toto teraz odkomunikujeme Dartu. Najprv definujeme typ pre feeding function:

typedef num Feeder(Animal a);

takto definovaný typ môžme použiť nasledovne:

 num feedAll(List<Animal> animals, Feeder feedOne){ //blah

a je to. Opäť, Dart nám na náš typ v podstate kašle, celý typový cirkus je o čitateľnosti a podpore, ktorú dostaneme od DartEditora.

Generics

večný problém. Začnime obligátnym úvodom pre tých, čo ešte nepočuli o:

Kovariancia a kontravatriancia typov

Zabudnime na chvíľu na Dart a vezmime UOOL (Univerzálny Objektovo Orientovaný Lenguidž). Oddeďme z triedy Animal triedu Cat a poďme filozofovať. Každá inštancia Cat is Animal, alebo inak: Cat ponúka všetky metódy čo Animal plus dačo navyše. So far so good, v akom vzťahu ale budú List<Cat> a List<Animal>? A aby to neznelo ako púha filozofická otázka: Môžme napr. funkcii feedAll, (očakávajúcej prvý argument typu List<Animal>) podhodiť parameter typu List<Cat>?

Na prvý pohľad áno, môžme, to je ale len zdanie. Čo ak by feedAll vytvorila nový objekt animal typu Animal (ale už nie Cat) a chcela ho pridať do obdržaného listu mačiek? To je akcia zrelá na Exception! Z pohľadu feedAll je ale všetko v poriadku (pridáva animal do List<Animal>). Celé zle. A aby náhodou nevzniklo zdanie, že sa jedná o collection-only problém:

var catFeeder = (Cat cat) => 0;

je pravda, že catFeeder is Feeder, resp., mal by byť catFeeder validným argumentom pre feedAll (definované vyššie)?

Videl som už viaceré riešenia tohto problému, s každým sa plus mínus dá žiť, ale žiadne ma neokúzlilo. Najzaujímavejšie sa k problému postavila Scala, ktorej autori múdro používajú vedecké pojmy ako kovariantný a kontravariantný, premakali typesystem a všetko čo sa nepodarilo vyriešiť, prehlásili za feature (kto si myslí, že Scala je dokonalá, nech mi napr. skúsi vysvetliť, prečo immutable Set nie je vo svojom type kovariantná).

Generics a Dart

Bez checked módu pracujeme ako v hociakom inom dynamickom jazyku, teda, robíme, čo chceme, pokiaľ neprídeme k problému ako objekt X nemá property Y, všetko bude OK. Pokiaľ zapneme checked mód:

typedef num oneFeeder(Animal);

void main() {
  var catFeeder = (Cat cat) => 0;
  var sthFeeder = (Something sth) => 0;

  print(catFeeder is Feeder); //true
  print(sthFeeder is Feeder); //true

  print(new List<Cat>() is List<Animal>); //true
  print(new List<Object>() is List<Animal>); //false
}

teda, pri typoch funkcií ignorancia typov arguementov, v kolekciách defaultná kovariancia, teda, List<Animal> je supertype List<Cat> preto, lebo Animal je supertype Cat.

Ako to bolo s tým krájaným chlebom

Dartovský typesystem má niečo do seba. Mám v Darte už čosi odkódené a (zatiaľ) som  optimistický: systém pomáha v orientácii v produkčnom kóde, nebrzdí pri prototypovaní, navyše, prakticky nikdy nemusíte bojovať s kompilátorom, aby láskavo pochopil, že váš dobrý kód (z pohľadu dynamického interpretera) je naozaj dobrý (z pohľadu statickej kontroly typov). Väčšinu typových problémov, na ktoré pri písaní Dartovského kódu narazíte, bude mať na svedomí DartEditor, ktorý má v typovej inferencii level UnderBeginner – a má čo doháńať. Pre tých, ktorí rozmýšľajú, čím sa bude treba vyhrážať kolegom/zamestnancom, aby písali do kódu typové anotácie: tým istým, čo používate na to, aby písali dokumentáciu, alebo testy – Dartovské typy sú tak trochu práve týmto.

Komentáře: 4

Přehled komentářů

Honza
Tomáš Kulich Re:
Radek Miček Re:
Honza Re:
Zdroj: https://www.zdrojak.cz/?p=10355