Chociaż moje pytanie może być zupełnie nieistotne, ale wyczułem wzorzec między większością języków programowania a ich oficjalnymi implementacjami.
Języki interpretowane ( interpretowane bajtowo?), Takie jak Python, Lua itp., Zazwyczaj mają wyjątkowo łagodną i łatwą składnię i generalnie nie zawierają żadnych typów lub nie wymagają od programisty jawnego pisania typów zmiennych w kodzie źródłowym;
Skompilowane języki, takie jak C, C ++, Pascal itp., Zwykle mają ścisłą składnię, generalnie mają typy i przeważnie wymagają więcej kodu / czasu programowania
Języki, których oficjalne implementacje są skompilowane w JIT, takie jak Java / C #, zazwyczaj stanowią unikalny kompromis między powyższymi dwoma, a niektóre z nich mają najlepsze cechy.
Niektóre z bardziej nowoczesnych skompilowanych języków programowania, takie jak D i Vala (oraz implementacja Java GNU GJC) są być może wyjątkiem od tej reguły i przypominają składnię oraz funkcje języków skompilowanych w JIT, takich jak Java i C #.
Moje pierwsze pytanie brzmi: czy to jest naprawdę istotne? Czy to tylko zbieg okoliczności, że większość interpretowanych języków ma łatwą składnię, te skompilowane w JIT mają umiarkowaną składnię i funkcje itp.
Po drugie, jeśli to nie przypadek, to dlaczego tak jest? Na przykład, czy niektóre funkcje można zaimplementować tylko w języku programowania, jeśli, powiedzmy, kompilujesz JIT?
źródło
Odpowiedzi:
Nie ma żadnego związku między semantyką i składnią. Języki skompilowane w Homoiconic, takie jak Scheme, mają dość minimalistyczną składnię. Meta-języki skompilowane na niskim poziomie, takie jak Forth, są jeszcze prostsze. Niektóre ściśle skompilowane języki kompilowane są oparte na trywialnej składni (think ML, Haskell). OTOH, składnia Pythona jest bardzo ciężka pod względem szeregu reguł składniowych.
I tak, pisanie nie ma nic wspólnego ze składnią, jest po stronie semantyki języka, chyba że jest to coś tak wypaczonego jak C ++, w którym nie można nawet parsować bez posiadania wszystkich dostępnych informacji.
Ogólną tendencją jest to, że języki, które ewoluowały zbyt długo i nie zawierały żadnych zabezpieczeń projektowych przed odchyleniami składni, prędzej czy później przekształciłyby się w obrzydliwości składniowe.
źródło
type checking
” jest wyjątkowo przeciwny do zamierzonego i nie powinien być używany, jest bardzo mylący, nie odzwierciedla charakteru systemów typu.Przeważnie jest to zbieg okoliczności.
Języki programowania ewoluowały z biegiem czasu, a technologia kompilatorów i tłumaczy poprawiła się. Wydajność bazowego przetwarzania (tj. Czas kompilacji, koszty tłumaczenia, czas wykonania itp.) Jest również mniej ważna, ponieważ główne platformy obliczeniowe wzrosły.
Składnia języka nie mają wpływu - na przykład Pascal został bardzo starannie zaprojektowany tak, to może użyć pojedynczego przejścia kompilator - czyli jeden podaniem źródła i masz plikiem wykonywalnym kodu maszynowego. Z drugiej strony Ada nie zwracała na to uwagi, a kompilatory Ada są niezwykle trudne do napisania - większość wymaga więcej niż jednego przejścia. (Jednym z bardzo dobrych kompilatorów Ady, z których korzystałem wiele lat temu, był kompilator 8-przebiegowy. Jak można się domyślić, był bardzo powolny).
Jeśli spojrzysz na stare języki, takie jak Fortran (skompilowany) i BASIC (zinterpretowany lub skompilowany), mają one / miały bardzo ścisłe reguły składniowe i semantyczne. [W przypadku BASIC, to nie jest stary BASIC Bills, musisz wrócić wcześniej do oryginału.]
Z drugiej strony, patrząc na inne starsze rzeczy, takie jak APL (świetna zabawa), to było coś w rodzaju dynamicznego pisania. Został również ogólnie zinterpretowany, ale można go również skompilować.
Łagodna składnia jest trudna - jeśli oznacza to, że masz rzeczy opcjonalne lub można je wywnioskować, oznacza to, że język ma wystarczające bogactwo, aby można go było ubić. Z drugiej strony, BASIC miał to wiele lat temu, kiedy zdanie „LET” stało się opcjonalne!
Wiele pomysłów, które teraz widzisz (na przykład pisanie bez czcionek lub dynamiczne) jest w rzeczywistości bardzo starych - pojawiło się po raz pierwszy w latach siedemdziesiątych lub wczesnych osiemdziesiątych. Sposób ich użycia oraz języki, w których są używane te pomysły, zmieniły się i wzrosły. Zasadniczo jednak większość nowości to tak naprawdę stare rzeczy przebrane w nowe ubrania.
Oto kilka przykładów z mojej głowy:
Mógłbym kontynuować.
Aktualizacja: Ponieważ nie byłem wystarczająco jasny.
Pisanie może się znacznie różnić.
Typowe statyczne pisanie w czasie kompilacji jest powszechne (np. C, Ada, C ++, Fortan itp.). Tutaj deklarujesz RZECZ TYPU i tak jest na zawsze.
Możliwe jest także dynamiczne pisanie, w którym rzecz wybiera typ, który jest do niej przypisany. Na przykład PHP i niektóre wczesne BASIC oraz APL, w których przypisujesz liczbę całkowitą do zmiennej i od tego czasu jest to liczba całkowita. Jeśli później przypisałeś mu ciąg, to był to typ ciągu. I tak dalej.
A potem jest luźne pisanie, na przykład PHP, w którym można robić naprawdę dziwaczne rzeczy, takie jak przypisać liczbową liczbę całkowitą (cytowaną, więc jest to ciąg znaków) do zmiennej, a następnie dodać do niej liczbę. (np. „5” + 5 spowoduje 10). To kraina dziwacznych, ale czasami bardzo, bardzo przydatnych.
JEDNAK są to funkcje zaprojektowane w języku. Wdrożenie właśnie to sprawia.
źródło
Myślę, że jest na odwrót: implementacja zależy od składni. Na przykład jeśli twoja składnia pozwala na refleksję, implementacja musi zapewnić środowisko wykonawcze, które to obsługuje.
źródło
Ogólnie zgadzam się z szybko_nieś, że twoja obserwacja jest głównie wynikiem historii. To powiedziawszy, podstawowe rozumowanie sprowadza się do czegoś takiego:
(Naprawdę nie jest to cytat, tylko moje własne sformułowanie.) Kiedy piszę
comfortable
tutaj, odnoszę się do tego, co nazwałeśbest features of both
. Mówiąc dokładniej, nie chcę wypowiadać się za lub przeciw pisaniu statycznemu / dynamicznemu lub ścisłej / łagodnej składni. Zamiast tego ważne jest, aby skupić się na programistach i zwiększyć ich poziom komfortu podczas pracy z językiem.Oto kilka powodów, które nie zostały wymienione w poprzednich odpowiedziach, które mogą dostarczyć ci pomysłów na to, dlaczego obserwujesz te rzeczy (i wszystkie są oparte na historii programowania programowania języków):
Obecnie mamy setki języków programowania. Kiedy pojawia się nowy, jak może znaleźć szerokie grono odbiorców? To jest główny powód, dla którego nowe języki zawsze starają się podnieść poziom komfortu programistów. Jeśli język może zrobić to samo co starszy, ale może to zrobić znacznie łatwiej / prościej / bardziej elegancko / etc. możesz rozważyć zmianę.
Krzywa uczenia się idzie w parze z tym. W przeszłości mieliśmy niewiele języków i poświęcenie czasu na naukę jednego było tego warte. Nawet jeśli oznaczało to zainwestowanie dużo czasu. Ponownie zwiększa się komfort, jeśli wpadniesz na język, którego programiści mogą się bardzo szybko nauczyć. Jakakolwiek złożoność (np. Skomplikowana składnia) jest szkodliwa, a zatem jest coraz bardziej zmniejszana w nowszych językach.
Postęp technologiczny (tutaj bezpośredni historyczny powód) jest odpowiedzialny za to, że twórcy kompilatorów mogą teraz kłaść większy nacisk na komfort programisty. Na początku cieszyliśmy się, że mogliśmy w ogóle zbudować kompilator. Często wiązało się to jednak z poważnymi ograniczeniami. W miarę wzrostu technologicznego know-how byliśmy w stanie ponownie znieść te ograniczenia.
Ogólnie rzecz biorąc, w językach programowania i kompilatorach zaobserwowano rozwój podobny do typowych aplikacji dla użytkowników końcowych:
źródło
(Not a quote really, just my own formulation.)
Cóż, sformatowałeś go jako kod, a nie jako cytat, więc nie sądzę, żeby ktokolwiek myślał, że to cytat :)Dany język programowania może, ale nie musi, ujawniać lub ograniczać wystarczającą ilość informacji semantycznych dla kompilatora, aby wydedukować, jak zredukować go do kodu wykonywalnego bez dodawania decyzji w czasie wykonywania („jaki typ jest ta zmienna?” Itp.). Niektóre języki są jawnie zaprojektowane do tworzenia ograniczenie to jest obowiązkowe lub łatwe do ustalenia.
Gdy kompilatory stają się mądrzejsze, mogą być w stanie odgadnąć lub sprofilować wystarczającą ilość informacji, aby wygenerować kod wykonywalny dla najbardziej prawdopodobnych ścieżek, nawet dla języków, które nie zostały wyraźnie zaprojektowane w taki sposób, aby ujawniać lub ograniczać te decyzje.
Jednak języki, w których kod (evalString ()) może zostać utworzony lub wprowadzony w czasie wykonywania (i inne rzeczy, których kompilator nie może wydedukować ani odgadnąć) mogą wymagać dostępności interpretera lub kompilatora JIT w czasie wykonywania, nawet przy próbach wstępnego skompiluj je.
W przeszłości język programowania i jego implementacja mogły ewoluować tak, aby pasowały do pewnych ograniczeń sprzętowych, takich jak to, czy interpreter może zmieścić się w 4k lub 16k, lub czy kompilator może zakończyć się w mniej niż minutę czasu procesora. Gdy maszyny stają się coraz szybsze, stało się możliwe (ponowne) skompilowanie niektórych wcześniej interpretowanych programów tak szybko, jak programiści mogą nacisnąć klawisz powrotu lub interpretować kod źródłowy wcześniej skompilowanego programu szybciej niż nieco starszy sprzęt mógł uruchomić zoptymalizowane skompilowane pliki wykonywalne.
źródło