2014-12-09 10 views
6

Pracuję z językiem Scala przez kilka miesięcy i już stworzyłem kilka projektów w Scali. Znalazłem Scala REPL (przynajmniej jego implementacja arkusza IntelliJ) jest dość wygodne do szybkiego rozwoju. Mogę napisać kod, zobaczyć, co robi i jest miło. Ale robię procedurę tylko dla funkcji (nie dla całego programu). Nie mogę uruchomić aplikacji i zmienić jej na miejscu. A przynajmniej nie wiem jak (więc jeśli wiesz, że możesz mi doradzić).Jaka jest różnica między Clojure REPL i Scala REPL?

Kilka dni temu mój współpracownik powiedział mi o Clojure REPL. Używa Emacsa do procesu programowania i może zmienić kod na miejscu i zobaczyć wyniki bez ponownego uruchamiania. Na przykład uruchamia proces i jeśli zmienia on implementację funkcji, jego kod zmieni jego zachowanie bez restartu. Chciałbym mieć to samo z językiem Scala.

P.S. Nie chcę dyskutować ani o tym, który język jest lepszy, ani o programowaniu funkcjonalnym lepiej niż obiektowo zorientowanym. Chcę znaleźć dobre rozwiązanie. Jeśli Clojure jest lepszym językiem dla tego zadania, niech tak będzie.

+2

Jeden jest używany do pisania Clojure, drugi do pisania Scala. Ponadto: http://stackoverflow.com/questions/2471947/is-there-an-easy-way-to-get-the-scala-repl-to-reload-a-class-or-package – vptheron

+0

Trochę trudno bym wiedział, jak będzie wyglądała poprawna odpowiedź? Być może pytasz, czy Scala REPL może ponownie zdefiniować funkcje w uruchomionym programie? –

+0

Ciekawość, pytanie jest dobre, ale tytuł nie jest. – Mars

Odpowiedz

18

Krótka odpowiedź brzmi, że Clojure został zaprojektowany do korzystania z bardzo prostego kompilatora z pojedynczym przejściem, który odczytuje i kompiluje pojedyncze wyrażenie lub formularz na raz. Na lepsze lub na gorsze nie ma informacji o typie globalnym, globalnej inferencji typów ani globalnej analizy lub optymalizacji. Clojure wykorzystuje instancje clojure.lang.Var do tworzenia globalnych powiązań za pośrednictwem serii hashmap od symboli tekstowych do wartości transakcyjnych. def tworzy wszystkie tworzy powiązania w zasięgu globalnym w tej globalnej mapie wiążącej. Więc gdzie w Scali "funkcja" (metoda) zostanie rozwiązana do instancji lub metody statycznej na danej klasie JVM, w Clojure "funkcja" (def) jest tak naprawdę tylko odniesieniem do wpisu w tabeli powiązań var. Gdy funkcja jest wywoływana, nie ma statycznego łącza do innej klasy, zamiast tego var jest referencją pod nazwą symboliczną, a następnie dereferencji, aby uzyskać wystąpienie obiektu clojure.lang.IFn, który jest następnie wywoływany.

Ta warstwa pośrednia oznacza, że ​​możliwa jest ponowna ocena tylko jednej definicji na raz, a ponowna ocena staje się globalna widoczna dla wszystkich klientów ponownie zdefiniowanego pliku var.

Dla porównania, gdy definicja w Scali się zmienia, skalak musi ponownie załadować zmieniony plik, makrozwróć, wpisać zwrot, sprawdzić typ i skompilować. Następnie ze względu na semantykę ładowania klas w maszynie JVM, skalak musi również ponownie załadować wszystkie klasy, które zależą od metod w klasie, które uległy zmianie. Również wszystkie wartości będące instancjami zmienionej klasy stają się koszami.

Oba podejścia mają swoje mocne i słabe strony. Oczywiście podejście Clojure jest łatwiejsze do wdrożenia, jednak płaci się stały koszt pod względem wydajności ze względu na ciągłe operacje wyszukiwania funkcji zapomnieć o problemach z poprawnością ze względu na brak typów statycznych i co ty. Jest to prawdopodobnie odpowiednie dla kontekstów, w których wiele zmian dzieje się w krótkim czasie (rozwój interaktywny), ale jest mniej odpowiednie dla kontekstu, gdy kod jest w większości statyczny (wdrożenie, stąd Oxcart). some work I did sugeruje, że spowolnienie w programach Clojure z braku statycznego łączenia linek jest rzędu 16-25%. To nie nazywa się Clojure slow lub Scala szybko, mają tylko inne priorytety.

Scala postanawia wykonać więcej pracy z góry, aby skompilowana aplikacja działała lepiej, co jest prawdopodobnie bardziej odpowiednie do wdrożenia aplikacji, gdy nastąpi niewielkie przeładowanie lub nie zostanie wykonane żadne ponowne ładowanie, ale udowodni, że przeciąga się, gdy chce wprowadzić wiele drobnych zmian .

Niektóre materiały mam pod ręką na temat kompilacji kodu Clojure mniej więcej kronologicznie według kolejności publikacji, ponieważ Nicholas wpłynął na moją pracę z GSoC.

Które przypuszczam pozostawia mnie nieszczęśliwe miejsce powiedzenia po prostu "Przepraszam, Scala nie została zaprojektowana w ten sposób C lojure było "w odniesieniu do szyfrowania wymiany".

+1

Gilad Bracha i inni luminarze ze światów Smalltalk i Lisp mają tę mantrę, że "Typy statyczne się łączą" i jest to inny przykład. To jest - przynajmniej według najlepszej naszej obecnej wiedzy - po prostu niemożliwe jest wdrożenie żywego środowiska dla statycznie napisanego języka, podczas gdy dynamicznie typowane języki mają * niezwykle * potężne, żywe środowiska, sprawdź MIT Lisp Machine, Squeak/Pharo Smalltalk , Newspeak, Lively Kernel Dana Ingall'a lub Self World. –

+0

Dziękuję wszystkim bardzo! – Curiosity

+0

@ JörgWMittag To zawsze kompromis między byciem dynamicznym i statycznie sprawdzalnym. Czasami chcesz jeden, czasem chcesz drugiego. Opieram się na statycznej stronie pisania - głównie dlatego, że widziałem sporo błędów, które występują nawet w Javie, ponieważ jego system typu jest zbyt łagodny. Nie oznacza to, że korzyści płynące z pisania dynamicznego są dla mnie nieważne, dla mnie zalety pisania statycznego po prostu ważą więcej. YMMV – Cubic