Dlaczego Java nie jest używana jako język kompilacji?

24

Jeśli Java jest językiem ogólnego przeznaczenia, a budowanie programu jest czymś, co można opisać za pomocą języka Java, dlaczego nie jest to najlepszy sposób pisania plików kompilacji, a zamiast tego używamy narzędzi takich jak Ant, Maven i Gradle? Czy nie byłoby to prostsze, a także nie wymagałoby nauki kolejnego języka programowania? (BTW - to pytanie można również zastosować do innych języków, takich jak C #)

vainolo
źródło
7
Możesz mieć nieco bardziej interesujące pytanie dotyczące „dlaczego nie są używane języki ogólnego przeznaczenia w językach kompilacji” - to znaczy, C też nie jest językiem kompilacji. Sugerowałbym również przyjrzenie się ceremonii wokół kompilacji jednego pliku .java w Javie
8
Prawdopodobnie można to uogólnić jako „Po co zawracać sobie głowę DSL?”
FrustratedWithFormsDesigner
1
Lepszym pytaniem może być; dlaczego IDE / kompilatory / narzędzia są tak złe, że w pierwszej kolejności potrzebne są narzędzia do budowania.
Brendan
6
@Brendan to nie jest pytanie, ponieważ narzędzia do budowania i IDE służą różnym celom.
jwenting
1
Ponieważ nigdy nie należy używać języków „ogólnego przeznaczenia” zamiast dobrze zdefiniowanych, prostych języków specyficznych dla domeny. A jeśli potrzebujesz nauczyć się „jeszcze jednego języka programowania”, nie powinieneś tak naprawdę programować, spróbuj innego handlu.
SK-logic

Odpowiedzi:

21

Określone narzędzie do określonego celu

  • Gadatliwość

    Języki ogólnego przeznaczenia są często zbyt szczegółowe. Gdybym miał zarządzać kompilacją w Javie, bardzo szybko wpadłbym w depresję ze względu na rozmiar bestii. Można go jednak łatwo zarządzać za pomocą DSL napisanego w Javie. I do pewnego stopnia tak można zobaczyć Gradle (dla Groovy) i Buildr (dla Ruby).

  • Trudność

    Języki ogólnego przeznaczenia są trudne. Cóż, nie sądzę, aby programowanie było trudne i nie mogło być odebrane przez nikogo, ale chodzi o to, że inżynier budowy niekoniecznie jest twoim programistą!

  • Cel, powód

    To mniej więcej na styku trudności i gadatliwości . Język został zaprojektowany w określonym celu, a tutaj mówimy o czymś bardzo specyficznym. Dlaczego więc miałbyś chcieć używać języka ogólnego ? Do kompilacji potrzebujesz:

    • gałęzie i warunki warunkowe do obsługi oddzielnych konfiguracji, środowisk itp.
    • zestaw instrukcji do wydobywania danych ze środowiska,
    • zestaw instrukcji do tworzenia rezultatów.

    Tak naprawdę nie potrzebujesz dużo więcej.

Na pewno jest denerwujące, gdy wydaje się, że twój system kompilacji nie jest wystarczająco elastyczny dla tego jednego specjalnego zastosowania, którego szukasz, ale prawdopodobnie jest znacznie lepszy niż alternatywa dla większości ludzi.

Prawdopodobnie dlatego istnieje polaryzacja między osobami, które wolą deklaratywne systemy kompilacji niż większość programowalnych jednostek: Myślę, że deweloper może mieć naturalną tendencję do szukania sposobów wyjścia z pudełka.

Czekaj, czy naprawdę potrzebujemy narzędzia?

Kolejne powiązane pytanie brzmi: czy naprawdę potrzebujemy narzędzia do kompilacji? Czy fakt, że istnieją we wszystkich językach, nie jest znakiem, że wypełniają lukę, której w ogóle nie powinno być w ogóle?

Niektóre języki niekoniecznie wymagają narzędzia do kompilacji. Na przykład większość języków skryptowych nie potrzebuje jednego i jest rozwiązywana w czasie ładowania. Albo weź Go, którego kompilator poradzi sobie z wszystkim, co jest fajnym kontrapunktem: co, jeśli kompilator taki jak gcc nagle nie potrzebuje wiązki flag, linkera i makefile, aby umrzeć razem? A jeśli javac nie potrzebuje pliku build.xml lub pom.xml, aby powiedzieć mu, co ma robić? Czy zarządzanie zależnościami nie powinno być bezpośrednio częścią własnego narzędzia języka, ponieważ zależności są częścią końcowego programu?

Z pewnością wygląda to na znacznie prostsze podejście dla użytkownika (konstruktora). Choć można argumentować, że robią to pod maską i zabierają ci wybór i możliwości wpływania na proces kompilacji (ale masz nadzieję, że takie narzędzie pozwala na rozszerzenia kompilatora i podobne rzeczy). Poza tym postrzegaliśmy narzędzia i język jako dwie odrębne rzeczy, więc może wydawać się nieczystym, że nagle ma tak ściśle powiązane.

Nie sądzę, że język, którym używasz do tworzenia programów, jest problemem. To język, którego używasz do programowania, jego podstawowa platforma i narzędzia, które powinny mieć znaczenie, a my wciąż robimy postępy.


Osobiście użyłem make / gmake / autotools / pmk i byłem zadowolony z nich dla C, i zacząłem od Javy, kiedy wszystko, co mieliśmy, było potem, a potem mrówka, a teraz ogólnie wolę Maven od wszystkich tych alternatyw. Chociaż widzę wartość w stopniach, budowniczych i innych, ale jak dotąd podoba mi się przewaga maven, dopóki nie nastąpi bardziej znacząca zmiana. Ponadto podoba mi się to , że jest sztywny, ale nadal pozwala ci obejść to w razie potrzeby. To, że to nie jest łatwe, jest dobrą rzeczą.

To narzędzie do budowania. Naucz się go obejmować i nie walcz z nim. To przegrana bitwa. A przynajmniej bardzo długi.

Haylem
źródło
Naprawdę podoba ci się twój pragmatyzm. Moje pytanie jest z ciekawości. Używam maven (w przeszłości używałem marki i mrówki) i robi ona „czarną magię”, która czasem jest dobra, a czasem zła. Ale to wciąż magia.
vainolo
1
„Lub jeśli javac nie potrzebuje pliku build.xml lub pom.xml, aby powiedzieć mu, co ma robić?” - heh. nie robi i nigdy nie zrobił.
user253751,
@immibis: to dyskusyjne. Naprawdę nie podobałoby mi się budowanie moich projektów w biurze przy użyciu samego javaca :) Z pewnością wymagałoby to sporo kleju za pomocą Makefile lub skryptu powłoki, a przynajmniej aliasów powłoki, aby wszystko połączyć. Lub gigantyczny program z wszystko w jednym folderze i wszystko zależy od repozytorium. Nie bardzo chciałbym! :) Ale to kolejna debata całkowicie niezwiązana z pytaniem PO.
haylem
Integracja systemu kompilacji z językiem jest również problematyczna w przypadku projektów poliglotycznych. Jeśli używam obu języków A i B i chcę mieć dla nich jeden proces kompilacji, jakiego systemu kompilacji używam?
Sebastian Redl
@SebastianRedl Więc w jaki sposób problem staje się łatwiejszy dzięki wprowadzeniu trzeciego języka? Po prostu wybierz język, który Ci odpowiada. (I oczywiście, jeśli potrzebujesz trzeciego języka, Java może być nim również.)
Ville Oikarinen
21

JavaJest to konieczne, język, Ant, Maven, itd. są deklaratywne w językach:

Możemy zdefiniować różnicę w następujący sposób:

  • Programowanie imperatywne: mówienie „maszynie”, jak coś zrobić, w wyniku czego stanie się to, co chcesz zrobić.
  • Programowanie deklaratywne: mówienie „maszynie”, co chcesz się wydarzyć, i komputer może wymyślić, jak to zrobić. 1

Języki kompilacji mówią konstruktorowi, co należy zrobić, skąd należy go wziąć itp. Silnik, który uruchamia kompilację (napisany w języku rozkazującym, jak zauważył @ElliottFrisch), czyta te instrukcje i je wypełnia.

Języki deklaratywne mogą wydawać się bardziej odpowiednie w scenariuszach kompilacji, ponieważ zadania kompilacji są ogólnie takie same i uważa się je za łatwiejsze w utrzymaniu i czytelne w tej formie niż w pełnoprawnej formie kodu.

Uri Agassi
źródło
3
Jest jednak co najmniej jeden system kompilacji, który używa imperatywnego języka. Scons używa Pythona.
user16764
Uczynienie go koniecznym nie jest trudniejsze niż napisanie klasy, która może zawierać listę zależności oraz metodę ich rozwiązania. Podejrzewam, że istnieje inny powód --- być może czas uruchamiania JVM.
2
@Lee: Prawie wszystkie narzędzia do budowania używane w świecie Java (Ant, Maven, Gradle, SBT,…) również zazwyczaj działają na JVM (z wyjątkiem Rake, Buildr lub Scons, które mogą , ale nie mają aby uruchomić na JVM).
Jörg W Mittag
6
@vainolo „Większość ludzi tak naprawdę nie lubi Ant / Maven / Make” naprawdę? To odważne stwierdzenie!
Ben Thurley
1
@BenThurley Lubię ludzi, dlaczego stworzono mrówkę? a jeśli mrówka była lubiana, dlaczego był maven? A teraz dlaczego ludzie używają Gradle? Ale zgadzam się, że jest to odważne stwierdzenie i jest poparte jedynie „anegdotycznymi” dowodami kilkudziesięciu programistów Java
vainolo
4

Jeśli spojrzysz na funkcje typowego systemu kompilacji, znajdziesz:

  1. Dużo danych: nazwy, przełączniki, ustawienia, elementy konfiguracji, ciągi itp
  2. Dużo interakcji ze środowiskiem: polecenia, zmienne środowiskowe
  3. Stosunkowo prosty „silnik kompilacji” do obsługi zależności, wątków, rejestrowania itp.

Jeśli zamierzasz napisać serię plików kompilacji przy użyciu jakiegoś języka (Java / C # / Python / etc), mniej więcej w trzeciej lub czwartej iteracji zdecydowałbyś się na (a) zachowanie większości danych i poleceń zewnętrznych jako danych w czymś jak XML (b) pisanie „silnika kompilacji” w twoim ulubionym języku.

Przydatne byłoby również traktowanie niektórych danych w pliku XML jako języka interpretowanego w celu uruchomienia różnych funkcji silnika kompilacji. Możesz również zinterpretować niektóre makra lub wykonać zamiany ciągów w danych.

Innymi słowy, skończyłbyś z Make, lub Ant, Rake lub MsBuild. Niezbędny język dla tego, co robi dobrze, i struktury danych opisujące to, co chcesz zrobić, teraz zwykle w formacie XML.

david.pfx
źródło
2

W takich przypadkach wiele czynników nie pozwala na użycie Java / C ++ / C #.

Po pierwsze, musisz skompilować skrypt kompilacji, zanim będziesz mógł go uruchomić, aby zbudować aplikację. Jak określiłbyś jakieś pakiety, flagi, wersje kompilatora, ścieżki narzędzi potrzebne do zbudowania skryptu kompilacji? Z pewnością możesz wymyślić sposób na obejście tego, ale o wiele prostsze jest posiadanie języka, który nie potrzebuje tego kroku kompilacji (np. Python), lub języka, który natywnie rozumie twoje narzędzie do budowania.

Po drugie, pliki kompilacji są obciążone danymi, podczas gdy Java / C ++ / C # są znacznie bardziej ukierunkowane na pisanie kodu i algorytmów. Java i przyjaciele nie mają zwięzłej reprezentacji wszystkich danych, które chcesz przechowywać.

Po trzecie, Java i przyjaciele potrzebują dużej ilości boksów, aby były ważne. Plik kompilacji musiałby znajdować się w metodzie wewnątrz klasy z wszystkimi jej importami. Korzystając z języka skryptowego lub niestandardowego, możesz uniknąć całego tego schematu i po prostu mieć same szczegóły kompilacji.

Winston Ewert
źródło
0

W rzeczy samej! Dlaczego nie użyć mocnego i wyrazistego języka dla problemu, który jest bardziej złożony niż ludzie (na początku) często myślą? Zwłaszcza gdy osoby, które borykają się z tym problemem, są już kompetentne w takim języku. (Budowanie jest własnym problemem programistów i najlepiej rozwiązane przez programistów.)

Zadałem sobie to pytanie wiele lat temu i zdecydowałem, że Java jest dobrym językiem do definiowania kompilacji, szczególnie dla projektów Java. W rezultacie zacząłem coś z tym robić.

ZASTRZEŻENIE : W tej odpowiedzi promuję iwant , system kompilacji, który rozwijam. Ale skoro i tak jest to dyskusyjna dyskusja, jestem pewien, że jest w porządku.

Nie będę omawiać korzyści płynących z Javy (moc i ekspresja), ani konkretnie. Jeśli jesteś zainteresowany, możesz przeczytać więcej na stronie iwant .

Zamiast tego zastanowię się, dlaczego Java (i inne GPL) jest tak łatwo odrzucana, że ​​nie nadaje się do budowania. Wiele odpowiedzi i komentarzy tutaj jest dobrym przykładem takiego myślenia. Rozważmy niektóre z typowych argumentów:

„Java jest niezbędna, ale kompilacje najlepiej definiować w sposób deklaratywny” , mogliby powiedzieć.

Prawdziwe. Ale kiedy używasz języka jako metalajzyka dla wewnętrznej DSL , tak naprawdę liczy się jego składnia . Nawet imperatywny język, taki jak Java, można oszukać, by zadeklarować. Jeśli wygląda i wydaje się deklaratywny, jest (dla celów praktycznych) deklaratywny. Na przykład:

JacocoTargetsOfJavaModules.with()
    .jacocoWithDeps(jacoco(), modules.asmAll.mainArtifact())
    .antJars(TestedIwantDependencies.antJar(),
            TestedIwantDependencies.antLauncherJar())
    .modules(interestingModules).end().jacocoReport(name)

To jest prawdziwy przykład z projektu demo iwant .

W rzeczywistości porównaj to z niektórymi rzekomo deklaratywnymi systemami kompilacji, które narażają swoich użytkowników na tak ważne czasowniki, jak „test” lub „kompilacja”. Powyższa deklaracja zawiera tylko rzeczowniki, bez czasowników. Kompilowanie i testowanie to zadania domyślnie obsługiwane przez iwant w celu zapewnienia użytkownikowi rzeczowników, których on chce. To nie jest język. Tak to wykorzystujesz.

„Java jest pełna”

Tak, dużo kodu Java jest tam gadatliwy. Ale znowu, to nie jest język, tylko sposób, w jaki go używasz. Jeśli implementacja jest pełna, po prostu obuduj ją za ładną abstrakcją. Wiele GPL zapewnia do tego odpowiednie mechanizmy.

Wyobraź sobie powyższy fragment kodu Java napisany w języku XML. Zastąp nawiasy nawiasami kątowymi i przesuwaj je. A następnie zduplikuj każde słowo kluczowe jako tag zamykający! Java jako składnia nie jest pełna.

(Wiem, porównywanie z XML-em jest jak zabieranie cukierków dziecku, ale tak się składa, że ​​tak wiele kompilacji zostało zdefiniowanych w XML).

„Musisz skompilować skrypt kompilacji”

To jest ważny punkt. Niemniej jednak jest to tylko niewielki problem techniczny do rozwiązania. Mógłbym to rozwiązać za pomocą fasolki szparagowej lub innego tłumacza. Zamiast tego rozwiązałem go, traktując go jako kolejny problem z kompilacją i ładując iwant za pomocą prostego skryptu powłoki lub ant, który kompiluje i uruchamia prosty bootstrapper Java.

„Java ma płytę grzewczą”

Prawdziwe. Musisz zaimportować klasy, musisz wspomnieć o „public”, „class” i tak dalej. I tutaj proste zewnętrzne DSL zapewniają początkową łatwą wygraną.

A jeśli twój projekt jest tak trywialny, że ta płyta jest znacząca, gratulacje. Twój problem nie jest trudny i naprawdę nie ma znaczenia, jak go rozwiązać.

Ale wiele projektów wymaga znacznie więcej niż tylko kompilacji, raportu pokrycia i pakowania. Jeśli płyta główna Java jest dopuszczalna w przypadku problemów klientów, dlaczego nie budować problemów? Po co robić buty tylko dla dzieci innych osób?

Ville Oikarinen
źródło
0

Jedną rzeczą, nie sądzę, że inne odpowiedzi dotyczyły tego, że ograniczenia i przejrzystość tych języków kompilacji stanowią ogromną część tego, co czyni je użytecznymi. Weźmy na przykład Maven. Tak, wykonuje kompilacje, ale także definiuje zależności. Pozwala to kompilacji Maven zlikwidować te zależności i spojrzeć na ich zależności i tak dalej.

Zastanów się, czy zrobiono to bezpośrednio z Javą. Narzędzie kompilacji zobaczy, że istnieje zależność. Musiałby wtedy ściągnąć inną aplikację Java i uruchomić ją, aby ustalić, jakie są zależności. Ale dla Maven po prostu patrzy na deklaracje zależności. Plik kompilacji maven jest przezroczysty. Kompletny język Turinga jest z natury nieprzejrzysty i dlatego jest gorszy w niektórych celach, takich jak ten.

JimmyJames
źródło
Jak pasuje do tego Gradle? Definiuje zależności w stylu deklaratywnym, używając języka ogólnego przeznaczenia (Groovy). W narzędziu opartym na Groovy, JavaScript lub Lisp naturalne jest używanie kompilatora lub interpretera języka do analizowania deklaracji, niezależnie od tego, czy chcesz je odczytać, czy „wykonać” (zastosować jakąś funkcję). Ta dualność kodu i danych nie jest częścią ogólnie przyjętego idiomu Java, choć nie jest niemożliwa. Kompletność Turinga nie uniemożliwia dobrej reprezentacji danych. Otwiera to możliwość, że twoje dane zrobią coś, czego się nie spodziewałeś.
joshp
@joshp Nie znam Gradle. Jest opisany jako DSL, a rzeczy wyglądają raczej deklaratywnie. Oparcie się na Groovy niekoniecznie oznacza, że ​​jego moc jest równoważna Groovy. Prawdopodobnie byłbyś w lepszej sytuacji, aby stwierdzić, czy Gradle jest w rzeczywistości językiem Turinga. Przykłady zależności, na które spojrzałem, wydają się dość proste. Czy możesz używać dowolnych wyrażeń Groovy w takich rzeczach, jak deklaracje zależności?
JimmyJames
Osobiście nawet nie lubię zależności przechodnich. Często powodują one niespodzianki w ścieżce klas (wiele niekompatybilnych wersji bibliotek). Dlatego maven ma element wykluczający, ale kłopot jest nie tylko tego wart. Wyraźne zależności ułatwiają życie. Nadal zależności przechodnie mogą być implementowane w Javie. Zrobiłem coś takiego z iwant , ponownie wykorzystując definicje kompilacji innych projektów.
Ville Oikarinen
@VilleOikarinen Zdarza mi się w większości zgodzić z tobą. Myślę, że powoduje to również mnóstwo wzdęć, ponieważ nie ma widocznego kosztu przyciągania większej liczby zależności, nawet jeśli ich nie używasz. Jednak w kontekście tej odpowiedzi nie komentuję wartości ani mądrości tej funkcji, ale tego, w jaki sposób użycie DSL jest przydatne w jej osiągnięciu.
JimmyJames
1
@VilleOikarinen Myślę, że dotarliśmy do końca, ale chcę tylko wyjaśnić, że nie mówię o pom / maven. To jest przykład kompilacji DSL, ale nie za bardzo o tym myślę. Używam go niechętnie i myślę, że jest niezgrabny. Ale tym, co pom jest tak dominujące, są deklaracje zależności. Używałem Buildr przez jakiś czas i czyta pom dla zależności, ale nie używa ich do specyfikacji kompilacji. Istnieje wiele narzędzi nie opartych na Javie, które rozumieją ten pom, ale AFAIK, wszystko, na czym im zależy, to zależności.
JimmyJames