Nie mam doktoratu, ani żadnego innego stopnia, ani z CS, ani z matematyki, ani też żadnej innej dziedziny. Nie mam wcześniejszego doświadczenia ze Scalą ani innym podobnym językiem. Nie mam doświadczenia w nawet zdalnie porównywalnych systemach typu. W rzeczywistości, jedynym językiem, że mam więcej niż tylko powierzchowną wiedzę z których nawet ma system typu jest Pascal, nie do końca znany ze swojego skomplikowanego systemu typu. (Mimo, że nie mają typów zakresu, który AFAIK prawie żaden inny język ma, ale to naprawdę nie jest istotne tutaj). Pozostałe trzy języki, których znam są podstawowe, Smalltalk i Ruby, z których żadna nie mają nawet system typu.
A jednak nie mam żadnych problemów ze zrozumieniem podpisu map
opublikowanej funkcji. Wygląda mi na prawie taki sam podpis, jak map
w każdym innym języku, jaki kiedykolwiek widziałem. Różnica polega na tym, że ta wersja jest bardziej ogólna. Wygląda bardziej jak C ++ STL niż, powiedzmy, Haskell. W szczególności abstrahuje od konkretnego typu kolekcji, wymagając jedynie podania argumentu IterableLike
, a także abstrahuje od konkretnego typu zwrotu, wymagając jedynie istnienia niejawnej funkcji konwersji, która może zbudować coś z tej kolekcji wartości wynikowych. Tak, jest to dość skomplikowane, ale tak naprawdę jest tylko wyrazem ogólnego paradygmatu programowania ogólnego: nie zakładaj niczego, czego tak naprawdę nie musisz.
W takim przypadku kolekcja map
nie musi być listą, uporządkowana, sortowalna ani nic podobnego. Jedyną rzeczą, na której map
zależy, jest to, że może uzyskać dostęp do wszystkich elementów kolekcji, jeden po drugim, ale nie w określonej kolejności. I nie musi wiedzieć, jaka jest wynikowa kolekcja, musi tylko wiedzieć, jak ją zbudować. Tak więc tego wymaga podpis typu.
Więc zamiast
map :: (a → b) → [a] → [b]
który jest tradycyjnym typem podpisu map
, uogólniono go, aby nie wymagał konkretnej, List
a jedynie IterableLike
struktury danych
map :: (IterableLike i, IterableLike j) ⇒ (a → b) → i → j
który jest następnie uogólniany poprzez wymaganie tylko istnienia funkcji, która może przekonwertować wynik na dowolną strukturę danych, jakiej chce użytkownik:
map :: IterableLike i ⇒ (a → b) → i → ([b] → c) → c
Przyznaję, że składnia jest nieco dziwniejsza, ale semantyka jest taka sama. Zasadniczo zaczyna się od
def map[B](f: (A) ⇒ B): List[B]
który jest tradycyjnym podpisem dla map
. (Zwróć uwagę, jak z uwagi na obiektową naturę Scali parametr listy wejściowej znika, ponieważ jest to domyślny parametr odbiornika, który ma każda metoda w systemie OO z pojedynczą wysyłką.) Następnie uogólniono go z konkretnego List
na bardziej ogólnyIterableLike
def map[B](f: (A) ⇒ B): IterableLike[B]
Teraz zastępuje IterableLike
kolekcję wyników funkcją, która generuje , właściwie, właściwie wszystko.
def map[B, That](f: A ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
W co naprawdę wierzę, nie jest to takie trudne do zrozumienia. Tak naprawdę potrzebujesz tylko kilku narzędzi intelektualnych:
- Musisz wiedzieć (z grubsza), co
map
jest. Przyznaję, że gdybyś podał tylko podpis typu bez nazwy metody, znacznie trudniej byłoby ustalić, co się dzieje. Ale skoro już wiesz, co map
należy zrobić i wiesz, jaki powinien być podpis typu, możesz szybko zeskanować podpis i skupić się na anomaliach, na przykład „dlaczego map
bierze to dwie funkcje za argument, a nie jedną?”.
- Musisz być w stanie odczytać podpis typu. Ale nawet jeśli nigdy wcześniej nie widziałeś Scali, powinno to być dość łatwe, ponieważ tak naprawdę jest to tylko mieszanka składni typów, które znasz już z innych języków: VB.NET używa nawiasów kwadratowych do polimorfizmu parametrycznego i używa strzałki do oznaczenia zwracany typ i dwukropek do oddzielenia nazwy i typu są w rzeczywistości normą.
- Musisz z grubsza wiedzieć, na czym polega programowanie ogólne. (Co nie jest , że trudno zorientować się, ponieważ jest to w zasadzie wszystko napisane w imieniu: to dosłownie programowanie w sposób ogólny).
Żadna z tych trzech nie powinna sprawić poważnego bólu głowy żadnemu profesjonalnemu, a nawet hobbystycznemu programistowi. map
był standardową funkcją w prawie każdym języku zaprojektowanym w ciągu ostatnich 50 lat, fakt, że różne języki mają różną składnię, powinien być oczywisty dla każdego, kto zaprojektował stronę internetową z HTML i CSS i nie można subskrybować nawet zdalnego programowania powiązana lista mailingowa bez denerwujących fanów C ++ z kościoła św. Szczepana wyjaśniających zalety programowania ogólnego.
Tak, Scala jest złożona. Tak, Scala ma jeden z najbardziej wyrafinowanych systemów typowych znanych człowiekowi, rywalizujących, a nawet przewyższających języki, takie jak Haskell, Miranda, Clean lub Cyclone. Gdyby jednak złożoność była argumentem przeciwko sukcesowi języka programowania, C ++ umarłby dawno temu i wszyscy pisalibyśmy schemat. Istnieje wiele powodów, dla których Scala najprawdopodobniej nie odniesie sukcesu, ale fakt, że programiści nie będą mieli kłopotów z włączeniem mózgu przed siadaniem przed klawiaturą, prawdopodobnie nie będzie głównym.