Mam chwilę czasu więc dam jeszcze jedna odpowiedź taka, która może trochę złagodzić twój start :)
1. Nawiasy
Clojure to LISP. Na początku nauki gdzie nie spojrzysz tam widać pełno nawiasów, które przez pierwszy miesiąc mocno utrudniają zapis kodu, a jeszcze mocniej odczyt kodu innych osób.
Na tym etapie najmocniej pomógł mi: paredit (wtedy kodowanie bardziej opiera się na stosowaniu wcięć, które automatycznie przesuwają nawiasy).
Oraz REPL w który ułatwił mi interpretację kodu innych osób. Po prostu z kodu wyciągałem podwyrażenia, wkładałem je do REPL i poprzez przekształcenia danych jakie zaobserwowałem szybciej łapałem co dana osoba miała na myśli.
Inna rzecz jaka jeszcze ułatwia spojrzenie na nawiasy to książka: SICP oraz Hackerzy i Malarze :D
2. Błędy
Druga rzecz jaka mnie niepokoiła to napotykane błędy. Nie są one oczywiste, często długie i czasem wymagają wiedzy z zakresu java/javascript. Nie będę ukrywał, ale na początku musisz być na to gotowy. To potrwa trochę nim zrozumiesz to co robisz źle :)
Rzecz jaka ułatwia przetrwanie to rozszerzenie które lepiej wyświetla traceback np. pyro oraz używaj REPL. Jak używasz REPL wówczas rzadziej popełnisz błąd, ponieważ twoje oprogramowanie nonstop ewoluuje. Jeśli popełnisz błąd łatwo jest namierzyć co źle zrobiłeś. Na tym etapie trochę ciężko jest mi wyjaśnić czym różni się to od zwykłego programowania. Programowanie kładzie nacisk na interakcję z programistą.
3. Wolne uruchamianie
Clojure wolno się odpala. Jezuu.. jak odpalam migrację to zdążę herbatę wstawić i wrócić nim migracja się skończy. :)
Dlatego zmień flow pracy z programem. Nie odpalaj clojure nonstop od nowa. Odpal raz i działaj w nim nonstop. Podepnij REPL do edytora i pisz program, i w REPL odpalaj konkretne funkcje/testy/zadania.
4. REPL
Ostatni krok na etapie przygotowań to REPL. Przestaw się jak najmocniej możesz na programowanie w REPL. Poprzednie punkty to tylko początek. Im wczesniej to zrobisz to tak jakby zrozumiesz szybciej jakie możliwości ma Clojure na dalszych etapach.
Np. wyobraź sobie, że możesz odpalić REPL i programować w nim program tak jakby to był ogranizm. Tzn możesz przekształcać program, obserować pośrednie stany, wprowadzać nowe funkcje, przekształcać je na żywo. Ogólnie program zmienia się w środowisko w którym rzeźbisz swój docelowy system.
Jednocześnie wiele rzeczy może wtedy ulec zmianie, możesz swój język zmienić tak by podporządkować go temu systemowi. Dla mnie takie programowanie ma niesamowitą moc rażenia, a to tylko początek tego wszystkiego.
5. Biblioteki
Gdy zaczniesz pisać swój program pewnie będziesz chciał użyć frameworka, biblioteki cokolwiek co przyspieszy Twoją prace. Tutaj są pewne odmienne zdania w środowisku clojure.
Pierwsze to jest fakt, że nie ma frameworków, tzn są ale nie są one tak popularne i nie odgrywają tak ważnej roli jak np. railsy w ruby, czy django w pythonie. Poza tym frameworki w clojure to bardziej zbiór skonfigurowanych bibliotek, by start był prostszy i tyle. Do webówki warto poznać Luminus.
W społeczności clojure bardziej ceni się pisanie mniejszych bibliotek (ale nie tak małych jak w nodejs).
To co może zaskoczyć to fakt, że wiele bibliotek w clojure swój ostatni commit miało 2-4 lata temu i co ciekawe taka biblioteka może być jak najbardziej w porządku. Ona nie jest zapomniana. Ona po prostu nie sprawia problemów i nie jest w nieskończoność rozszerzana.
Druga sprawa jeśli nie masz biblioteki w clojure to dzięki interop możesz bez problemu użyć kodu z javy / javascript. Ja do tej pory używałem bibliotek do manipulacji pdf i grafiką i nie napotkałem większych problemów.
6. Orientacja na dane
Programowanie w Clojure wymaga trochę innej postawy niż w OOP. O ile w innych językach coraz większy nacisk kładzie się na pompowanie abstrakcje, na hermetyzację, dziedziczenie, polimorfizm, interfejsy itp to w przypadku clojure nie jest to dobra droga.
Jeśli myślisz o modelowaniu czegoś to używaj wbudowanych typów. Clojure jest zorientowany na dane. I tutaj najlepiej jest myśleć, że dane to dane. I jak coś modelujemy to z użyciem: mapy map, wectorów map, zbióry liczb itp Nie próbuj nadmiernie się ubezpieczać tak jak w java, inaczej tylko na tym stracisz.
Wiele jest przeciwników tego podejścia (szczególnie osoby, które potrzebują jawnych typów, statycznej kontroli itp) - niestety dla nich to podejście jest mega uciążliwe, śliskie i trudne do zakceptowania.
Wbrew pozorom używanie typów wbudowanych ma parę genialnych w prostocie rzeczy.
Jeśli korzystasz z REPL to nonstop widzisz swoje dane. Jak program się zmienia to widzisz jakie były przejścia. To rozwiązanie fajnie sprawdza się jak masz front SPA i piszesz coś pokroju react/redux, ale też spoko jest gdy robisz coś z danymi z bazy, albo gdy pracujesz z requestami / responsami. Weź pod uwagę, że jak masz niemutowalne dane to może je współdzielić na kilka sesji przez nrepl. Masz program i możesz zdalnie do niego się podpinać i nim sterować. Coś takiego w praktyce pozwala na podpięcie do przeglądarki i trzymanie w niej stanu, jeśli stronka się zmienia, stan jest pamiętany, a ty dodatkowo możesz operować na stanie z poziomu dodatkowego terminala i własnych pomocniczych funkcji.
Inna rzecz to sposób zapisu programów. Jeśli wiesz, że twoje programy są zbudowane z prostych typów to możesz pisać funkcje tak, by w sygnaturce od razu dokonywały destruction - takie odpakowanie danych, byś miał już nazwane zmienne, które pochodzą z wnętrz struktury. Dzięki temu funkcje stają się znacznie prostsze w odczycie i zapisie.
7. Programowanie funkcyjne
Pisząc funkcyjnie ogólnie piszemy wyrażenia, które na podstawie danych wejściowych produktują dane wyjściowe.
Ze wględu, że dane są niemodyfikowalne to by zrobić jakąś operację najczęściej musisz myśleć o programie jak o pipelinach. Robisz je składając różne funkcje. Tutaj dobrze jest opanować kompozycje funkcji, threading macros, ponieważ takie rzeczy mocno wpływają na czytelność takiego programu.
Blog z którego czerpałem dużo wskazówek odnośnie zapisu kodu funkcyjnego to: https://stuartsierra.com
8. Lewniwe sekwencje
Wraz z budowaniem pipelinów o jakich wspomniałem w poprzednim punkcie ważnym pojęciem są leniwe sekwencje. Chciałbym dodać, że są one przereklamowane i ułatwiają napisanie błędnego kodu. Warto je używać z rozwagą.
Najbardziej typowy błąd wynika z odroczenia ewaluacji. Np. jeśli masz funkcje która ma blok try/catch a w środku mieli dane z użyciem leniwej sekwencji to ten try/catch może nie zadziałać, leniwa sekwencja natualnie bez wykonywania kodu może opuścić kontekst funkcję i dopiero jak pojawi się potrzeba ewaluacji to może pojawić się błąd, którego już niestety nie przechwyci try/catch. Między innymi podobne rzeczy dzieją się jak robi się asynchroniczne operacje i tzn dynamiczny binding.
Podsumowanie
Dobra notka robi się trochę przydługa, ale dodam, że to bardziej początek. Jest więcej tematów takich jak
jak kodowanie współbieżne, asynchroniczne z core.async obsługa frontu, react + funkcyjne kodowanie + figwheel <3, pisanie kodu wspólnego dla frontu i backendu, transducery, czy makra.
Końcąc dodam, że nie miałem możliwości pracy w tym języku dla innych firm :-(, ale za to tworzę od paru miesięcy własne komercyjne produkty w clojure/clojurescript i rzeczy jakie teraz najbardziej rzutują na pisanie kodu to ultra lekkie programowanie frontu (piszę coś w rodzaju ala CRM z wieloma dynamicznymi widokami jak w jira - to jest mega proste w odróżnieniu od tego co miałem w javascript) + możliwość kodowania wszystkiego w jednym spójnym języku (front + backend, a w planach mam nawet mobilne rzeczy).