Daniel Kolman

Dart: Lehký úvod pro programátory C#

| 1 comments |

Dart je kůl, pokud to ještě nevíte. Je to startovací droga dynamických jazyků pro programátory zvyklé na statické typování. Není tak dokonalý jako C#, ale i tak dosahuje vysoké úžasnosti a rozhodně je lepší než JavaScript. Ponechme nyní stranou předsudky a filosofické diskuse o užitečnosti nového jazyka a pojďme se podívat, jak Dart vypadá z pohledu programátora, který je léta navyklý užívat kód v C#.

Úvodem je nutné uvést na pravou míru jeden zažitý předsudek: Dart není pouze náhrada JavaScriptu. Dart má potenciál být plnohodnotným serverovým jazykem. Virtuální mašinu pro Windows si můžete stáhnout z DartForce (dart_bin.exe) a existuje už i runtime určený pro HTTP server, po vzoru node.js: Fling. Dart je mladý (nebo mladá?)  a tak je to všechno ještě v plenkách. Aplikaci v "produkční" kvalitě z toho nepostavíte, ale ukazuje to slibný směr, kterým se Google chce vydat: Jeden jazyk na serveru a v browseru (a doufejme že časem i na Androidu).

Nejjednodušší způsob, jak si vyzkoušet plain vanilla Dart, je online dartboard na try.dartlang.org. Také si můžete stáhnout editor, který umí jednoduché napovídání, navigaci, zvýraznění syntaxe a indikaci chyb při uložení souboru. Také dokáže zkonvertovat Dart na JavaScript a pustit váš program v browseru, ale také můžete psát jen .dart soubory a spouštět je z příkazové řádky ručně (vypadá to, že to již brzy bude umět přímo editor).

Dart je sice dynamický jazyk, ale má typové anotace a během vývoje lze zapnout checked mode, ve kterém provádí interpret před spuštěním kódu statické typové kontroly (viz zaškrtávací volba "Checked Mode" na stránce try.dartlang.org).

Základy

Dart je jednoduchý a stručný, přitom má ale všechny rysy jazyka, které činí C# lepší než Java (#joke). Syntax je podobná Javě i C# a je velmi intuitivní. Dart je objektový jazyk, ale nevyžaduje třídy:

// Dart
void main() {
  print('Hello World!');
}

// C#
using System;
namespace HelloWorld
{
  class Hello 
  {
    public static void Main() 
    {
      Console.WriteLine("Hello World!");
    }
  }
}

Jak je vidět, Dart nemá ani namespace, tedy možná zatím. Jednotlivé soubory se zdrojovým kódem lze seskupit do library, která má být základní jednotkou modularity. Při importu library můžete definovat nepovinný prefix, přes který budete přistupovat k typům v knihovně, a zabránit tak případným konfliktům s jinými knihovnami.

Dart zná jen dvě viditelnosti: public a private, které se zapisují konvencí – členy třídy, které jsou private, je nutné prefixovat podtržítkem. Na druhou stranu díky tomu odpadá velmi mnoho zbytečných klíčových slov, kód je stručnější a vynucuje se tak sdílený styl psaní kódu. Private v Dartu je to samé jako internal v C#, to znamená, že deklarace jsou viditelné uvnitř library.

Dart zná gettery a settery jako v C#, příjemné je, že se k nim přistupuje stejně jako k proměnným třídy, takže odpadá známé spartakiádní cvičení s backing fieldy nebo s autoproperty.

class User {
  String name;

  num _age;
  num get age() => _age; // Getter
  set age(num value) { // Setter
    if (value < 0) throw 'Prenatal clients are not accepted';
    _age = value;
  }
}

void main() {
  var user = new User();
  user.name = 'Karl';
  user.age = 42;
  print('${user.name} is ${user.age} years old.'); // proměnné lze psát přímo do stringu
}

Na tomto příkladu je také vidět pár dalších cukříků: Pokud je funkce jeden výraz, lze ji zapsat pomocí =>, jako v getteru age. Na předposledním řádku si všimněte, že proměnné jsou použity přímo jako parametry ve stringu a díky string interpolation se při přiřazení vyhodnotí.

Zajímavý syntax-sugar je automatická inicializace proměnných třídy přímo z konstruktoru:

class Point {
  final num x, y; 
  Point(num this.x, num this.y);  // automaticky nastaví x a y
}

void main() {
  var p = new Point(1,2);
  print('[${p.x}, ${p.y}]'); // vypíše [1, 2]
}

Mezi další pokročilé fíčury známé z C# patří nepovinné parametry s defaultní hodnotou a anonymní funkce, z JavaScriptu pak jednoduchá deklarace polí:

void foo([num answer = 42]) { // nepovinný parametr
  print('answer: $answer ');
}

void bar(void action(num x)) { // parametr typu funkce
  action(123);
}

void main() {
  foo();
  foo(56);

  bar((num x) { foo(x); }); // anonymní funkce

  var names = ['Karl', 'Egon', 'Franz']; // pole
  for(String name in names) { // foreach
    print(name);
  }
}

Optional Typing a generika

Klíčové slovo var v Dartu odpovídá dynamic v C#. V C# jsme zvyklí, že proměnná definovaná pomocí var má statický typ, odvozený od hodnoty. V Dartu má sice takto vytvořená proměnná také typ odvozený od hodnoty, ale může se změnit přiřazením jiné hodnoty, stejně jako dynamic v C#; nakonec Dynamic je v Dartu definován jako typ. Pokud chcete, aby vás kompilátor varoval při nesprávném přiřazení, musíte specifikovat typ explicitně:

void main() {
  var text = "x";
  print(text is String); // true
  text = 123; // tímto se změní typ proměnné text
  print(text is String); // false
  print(text is int); // true

  int n = 123;
  // následující řádek způsobí compile-time warning a runtime error:
  n = "aaa"; // warning: String is not assignable to int
}

Dart také podporuje generika, a to rovnou s kovariancí! Bohužel se typ parametru nekontroluje staticky, ale až při běhu programu, kdy vyhodí výjimku:

void printAllMembers(Iterable<Object> list) {
  for(var i in list) {
    print(i);
  }
}

main() {
  var integers = new List<int>();
  integers.add(1);
  integers.add(2);
  integers.add(3);
  integers.add('string'); // runtime error

  print(integers is List<num>); // true
  print(integers is List<Object>); // true
  print(integers is List<String>); // false

  printAllMembers(integers);

  List<Object> objects = integers;
  objects.add(4);
  objects.add('a'); // runtime error

  List<int> ints = new List<num>(); // runtime error
}

// PS: Object <- num <- int

A teď pozor, výchozím typovým parametrem, pokud není zadán, je Dynamic. A protože se v Dartu dá vše přetypovat na Dynamic, platí následující, i když to není nám, co jsme odkojeni staticky typovanými jazyky, hned zřejmé:

main() {
  print(123 is Dynamic); // true
  print("tralala" is Dynamic); // true

  List<int> numbers = new List<int>();
  print(numbers is List<Dynamic>); // true
  print(numbers is List); // true

  print(new List() is List<Dynamic>); // synonymum
  print(new List() is List<int>); // true!
}

Díky tomu můžete předat netypované pole metodě z knihovny, která v parametru vyžaduje typovaný seznam.

Pojmenované konstruktory

Dart má také inovativní prvky, které jsem nikdy v jiném jazyku neviděl (což neznamená že to v žádném nikdy nebylo). Dart nemá možnost přetížit deklaraci metody (overload), což může vadit u konstruktorů. Američtí inženýři si s tímto problémem poradili pomocí pojmenovaných konstruktorů:

class TimeSpan {
  final num seconds;
  TimeSpan(num this.seconds);
  TimeSpan.fromMinutes(num minutes) : this(minutes*60);
  TimeSpan.fromHours(num hours) : this.fromMinutes(hours*60);
}

main() {
  print(new TimeSpan(10).seconds); // 10
  print(new TimeSpan.fromMinutes(10).seconds); // 60
  print(new TimeSpan.fromHours(10).seconds); // 36000
}

Factories

Slibná vlastnost jsou factories. Například takto lze v Dartu napsat singleton:

class Hello {
  static Hello _instance;
  factory Hello() { // factory constructor
    if(_instance==null) {
      _instance = new Hello._internal();
    }
    return _instance;
  }

  Hello._internal();
}

main() {  
  var test1 = new Hello(); // z pohledu klienta žádná změna
  var test2 = new Hello();
  print(test1===test2); // true
}

Všimněte si, že není potřeba měnit použití třídy, takže když se rozhodnete, že u existující aplikace změníte lifetime scope některé třídy, nemusíte opravovat všechna místa, kde se instancuje. Kromě factory constructoru lze použít i factory třídu. Toho se hojně využívá u rozhraní, kde lze tímto způsobem deklarovat default implementaci. Například List není třída, ale interface s factory třídou, takže je možné ho přímo instancovat operátorem new:

interface List<E> extends Collection<E> factory ListFactory { ... }

class ListFactory {
  factory List<E>([int length = null]) {
    ...
    return new ListImplementation(...);
  }
}

var list = new List<int>();

The Future Is Dart

Dart je jazykem budoucnosti (teda okrem prípadov keď nie). Ale nechme si filosofování o užitečnosti a životaschopnosti Dartu někam k pivu. Pokud vás tento jazyk zaujal tak jako mě, mám pro vás pár základních odkazů na zdroje, ze kterých jsem čerpal:

Základním zdrojem pro začátečníky je dartlang.org, kde najdete technical overview, dartboard pro vyzkoušení Dartu online a v sekci Articles několik zajímavých článků. Zajímavý je Idiomatic Dart o konvencích, jak psát v Dartu jako v Dartu a ne jako v JavaScriptu, C#, Javě.

Pokud vás zajímá vývoj pro server, na DartWatch je série článků, jak vytvořit HTTP server a klientskou aplikaci.

Pokud chcete sledovat vývoj jazyka přímo u zdroje, na Dart Wiki je návod jak si nastavit vývojové prostředí (na DartWatch je podrobný tutorial, ale ten postupně začíná být out-of-date. Diskuzní skupinu naleznete zde.

(1) Comments

  1. Robert Dresler said...

    Pěkná motivační ochutnávka. Díky za fajn článek psaný s příjemnou lehkostí. Dobře se to četlo ;-)
    Jazyk Dart vypadá zajímavě a určitě se mu zkusím podívat víc na zoubek. Některé jeho rysy vypadají opravdu hodně zajímavě.

    9. prosince 2011 20:47

Leave a Response