Złożoność składni języków programowania

1

Czy większa złożoność składni języka programowania jak jest dla przykladu w C++ odbija się negatywnie na samym języku i programach w nim napisanych? Inaczej formuując pytanie, czy więcej ficzerów napchanych do danego języka programowania, wtedy kiedy można zrobić coś innymi metodami równa się gorzej?

1

Tak, nie, może. Przykładowo: W Javie 8 zostały wprowadzone lambdy, dzięki czemu nie trzeba robić klas anonimowych z jedną metodą. Jest to zwiększenie złożoności składni, ale trudno stwierdzić że to źle wpłyneło na Javę, pewnie gdyby nie to ja bym w tym języku nawet nie pisał xD
Można dodawac nowe featury do języka tylko z sensem a nie losowo.

4

Zależnośc ilości feature'ów do ilości składni nie musi być liniowa jak język jest dobrze zaprojektowany. Oczywiście w przypadku języka tak wiekowego jak C++ o dobrym zaprojektowaniu nie może być mowy ponieważ C++ na przestrzeni lat zbierał kolejne feature'y, wrezultacie jego składnia jest strasznie rozbudowana do ilości feature'ów. Mam wrażenie że składnia Rusta jest prostsza a ilość feature'ów podobna.
Podobna sytuacja jest w Scali, Scala 3 ma więcej feature'ów niż Scala 2 a jednocześnie ma prostszą składnię. Tak przynajmniej mówi twórca języka.
Dla porównania Scheme ma prostacką składnię (wszystko jest listą) a Racket (język który zaczynał jako implementacja Scheme, a teraz jest uważany za Scheme-like język) ma całkiem duże featurów (i chyba nawet daje wszystko tam jest listą, przynajmniej na poziomie parsowania)

0

Co to w ogóle znaczy "feature języka"? Wszystko ostatecznie sprowadza się do instrukcji procesora lub kodu pośredniego, wszystkie te nowe ficzery to lukry składniowe. Można z nich korzystać lub nie, jak coś takiego zmierzyć?

11

Moim zdaniem dużo ważniejsza jest spójność ficzerów niż ich liczba. Tzn jak dodajesz do języka jakieś losowe ficzery i potem masz kod który robi totalny misz-masz, to wtedy odbija się negatywnie.

4

Jak najbardziej sie odbija. Gro programistow nie ma czasu ani ochoty studiowac latami danego jezyka dlatego programuja tak jak umieja.
Jak jezyk wymaga zaawansowanej znajomosci jego szczegolow to powstaje ryzyko ze ktos popelni blad.
Albo tworca aplikacji, albo kompilatora.

Dlatego przez dlugi czas krolowal COBOL, ktory nie powinien byl powstac - bo kazdy moze sie go nauczyc w stosunkowo krotkim czasie i rzezbic w nim biznes rule. Ten rynek lubi prostote.

Java w zalozeniu jest obiektowa wersja C z uproszczeniami (brak unsigned, sensowne literaly stringowe, tablice z rozmiarem, interfejs bez implementacji - poczatkowo).
Po latach wychodzi ze te uproszczenia nie zawsze byly dobre - dodaja obsluge unsigned, interfejsy zyskuja metody domyslne).

Oby jezyki byly jak najprostsze.
Do znajomosci calego jezyka powinna wystarczyc jedna ksiazka.

1

Można dodawać co się chce, byle zachować kompatybilność wsteczną, jak w JavaScript.

2

Nie, weź sobie jakiś najprostszy składniowo język, np. Brainfuck i spróbuj napisać w nim coś większego. Trudność w cpp nie polega na tym, ze tam jest za dużo rzeczy w składni. Java praktycznie ma to samo, a jest dużo prostsza. Problem wynika z tego, że to co w Java robi za ciebie kompilator, albo framework w CPP musisz zrobić sam. Zarządzanie pamięcią, operacje na wskaźnikach nie mają nic wspólnego ze składnią języka. Druga sprawa, że w CPP jest od groma dziwactw zebranych przez ostatnie 50 lat i nigdy nie wiesz na jaki projekt trafisz. Pisząc dzisiaj coś od zera, nie jest to język trudniejszy od Javy i właściwie można przenieść interface'y 1:1. Zapakujesz zwracany obiekt w smart pointera i "masz Javę", no, może do bez refleksji. Ale z drugiej strony masz te 50 lat historii, kiedy wzorcem było coś, co dzisiaj jest anty-wzorcem (bo są lepsze metody rozwiązania takich problemów) a niektórzy programiści C++ stawiają sobie za punkt honoru napisanie kodu tak, żeby wykorzystać jakieś dziwactwa typu iterowanie po tablicy pointerem zmieniając jego adres o x bajtów,

1

To, że w jakimś języku można coś zrobić, nie znaczy, że każdy musi robić to samo w swoich projektach. Co z tego, że w jakimś projekcie C++ są wskaźniki, skoro ja do swojego prostego projektu kalkulatora ich nie potrzebuję? Poza tym rzeczy, które pojawiały się w języku z czasem, powstały według trochę innych założeń, niż te z samego początku języka. To nie znaczy, że te stare mechanizmy trzeba od razu komuś zabrać. A to, że jakiś programista pisał jak mu wygodnie albo pomieszał w projekcie kilka rzeczy, to wina tego programisty a nie samego języka. Można podchodzić tu w prosty sposób: albo piszemy tylko nowymi sposobami albo starymi. Mogą to byś wskaźniki w starej składni i odpowiedni im klasy. Nie trzeba mieszać obu sposobów.

0

Ile projektów w pracy zaczynałeś od 0? Moje doświadczenie z C++ jest takie, że zdarzyło się tak raz. A i tak było tam dociągnięte sporo kodu tworzonego przez ostatnie ~30 lat i był to kod z pełną historią języka. Żeby było jasne - to, że możesz wziąć taki kod i wciągnąć go do nowego projektu to zaleta, a nie wada.
Nie mam też pojęcia skąd wziął się wątek winy. Piszę jedynie o faktach. Wejście dzisiaj w proste projekty C/C++ jest proste. Popatrz na amatorskie projekty Arduino. Ale ktoś z taką wiedzą, nie poradzi sobie w projekcie komercyjnym.

3

Czy większa złożoność składni języka programowania

JS jest takim językiem, do którego ciągle dodają nową składnię, a jakoś mało kto narzeka, większość się cieszy. Mało tego, ludzie nawet jeszcze bardziej komplikują sobie JS dodając TypeScript, który z definicji jest bardziej skomplikowany niż JS.

Czyli w praktyce ludzie raczej się cieszą, że język ma skomplikowaną składnię.

Chociaż mnie akurat wkurza rozdmuchiwanie JSa, większość tych nowych ficzerów z JSa nie jest nawet potrzebna, a nawet jeśli jakieś są potrzebne, to większość programistów i tak nie będzie ich znać/rozumieć/umieć użyć. JS robi się jak przysłowiowe C++, o którym się mówi, że nikt go nie zna do końca.

Plus JS ma mnóstwo elementów starych albo nieprzemyślanych, których się nie da wywalić, a które potem kolidują z nowymi elementami. Robi się to niespójne jak PHP. Ale zaraz, patrząc na nowinki z PHP mam przynajmniej wrażenie, że tam dodają coś sensownego do języka.

Czyli ogólnie uważam, że JS powinien być zaprojektowany od nowa. Niestety tak się raczej nie stanie.

odbija się negatywnie na samym języku i programach w nim napisanych?

Ludzie zaczynają używać randomowych ficzerów z języka kompletnie bez sensu/potrzeby/zrozumienia, więc odbija się to jakoś negatywnie.

0

Generalnie, większa złożoność składni jest wprowadzana:

  1. W celu poprawy jakości generowanego kodu. Dajmy na to używanie atomikow z zadeklarowanym modelem pamięci może dać lepszy kod niż zahardkodowanie na volatailach
  2. W celu ułatwienia zarządzania dużymi projektami. Tu głównie OOP czy interfejsy
  3. W celu zmniejszenia ilości błędów. Np. strong typing
  4. W celu dogodnego udostępnienia optymalnych rozwiązań pewnych problemów - generyki
  5. W celu poprawy przenośności - elementy które są w standardzie języka, powinny działać wszędzie. Zakładanie istnienia niektórych bibliotek, jak POSIXowych threadów, już takie pewne nie jest

Różne języki mają różne sety ficzersów, a programiści ich używają bądź nie, bo w jednych projektach ich potrzebują a w innych maja wywalone na prędkość optymalność... a nawet na poprawność.

0

czy są jakieś mainstreamowe języki i ich libki które w ogóle mają jakiś deprecation plan i faktycznie go egzekwują?

np. od C# 10 kolekcje typu ArrayList są deprecated i zostaną faktycznie usunięte w 2025 lub jakieś słówka kluczowe

bo z jednej strony kompatybilność wsteczna, a z drugiej no ileż można xd później nowe osoby chcą się nauczyć danego języka i jego środowiska, to trzeba tłumaczyć: no właściwie to 10 lat temu było martwe, tego to nikt nie używa, bo od 5 lat jest lepszy sposób itp.

2

@WeiXiao: java po 20 latach się tego dorobiła. Adnotacja @Deprecated dorobiła się opcjonalnego atrybutu forRemoval czyli - serio, naprawde to usuniemy.

2

Myślę, że tak. Im więcej ficzerów tym większa szansa, że coś się zdezaktualizuje. Widać to zwłaszcza jak ficzery są dodawane stopniowo a szczególnie widać to w C++, który jest jednocześnie stary i mocno eksperymentalny, co jest idealną mieszanką na powstawanie potworków. To C++ wprowadził wiele ficzerów nie znanych w innych językach jak np. move semantic. Niestety działa to słabo, ale było to spowodowane tym, że C++ miał na swoich barkach legacy z C, gdzie każdy typ jest kopiowany i cały mechanizm był projetkowany w taki sposób, żeby nie zepsuć istniejącego kodu. Dzięki temu mógł przyjść Rust, który przeorał cały mechanizm tak, że przyjemnie się go używa i nie masz takiego uczucia w czasie programowania, że język chce, żebyś gdzieś popełnił jakiś głupi błąd.

Moim ulubionym ficzerem jest uniform initialisation, który można podsumować tak https://xkcd.com/927/

6
WeiXiao napisał(a):

bo z jednej strony kompatybilność wsteczna, a z drugiej no ileż można xd później nowe osoby chcą się nauczyć danego języka i jego środowiska, to trzeba tłumaczyć: no właściwie to 10 lat temu było martwe, tego to nikt nie używa, bo od 5 lat jest lepszy sposób itp.

Może należałoby co 10-15 lat wycinać jakiś podzbiór sensownych ficzerów i robić nową generację języka (nawet pod inną nazwą), zamiast pudrować jednego trupa w nieskończoność?

7

Projekt informatyczny jest ładny póki młody, potem potrzeby biznesowe i zmienność środowiska wymuszają zmiany, czasem robione na kolanie byleby załatać nagłą dziurę w samym środku, czasem kompletnie nie pasujące do reszty, acz jednak potrzebne, czasem chowające stary śmietnik pod nową warstwą, byleby go nie było widać. Czas płynie, entropia nieubłaganie rośnie, design się wykrusza i robi się stopniowo coraz to bardziej pokraczny twór, ciągnący za sobą długi bagaż doświadczeń. Jako taki język programowania nie różni się wielce od takiej apki i tyczą się go te same zasady i taka sama erozja stopniowo szoruje jego powierzchnią, dostarczając znużonym programistom argumentów do nie kończących się awantur, dysput i narzekania.

I takie właśnie odczucia mam patrząc na te współczesne języki, które wraz z upływem lat obrastają tłuszczem jak hodowlane prosiaki, byleby zaspokoić wiecznie nienasycone żądze nowych ficzerów, byleby tylko można było koniec końców zapisać w kodzie jedną linijkę zamiast trzech, bo inaczej jest przecież zbyt nieczytelnie. Język taki jak C++ oczywiście jest tu najbardziej pokiereszowany i poszkodowany, bo sięga pamięcią czasów iście antycznych, gdy komputery były duże, a programy małe i średnio się odnajduje we współczesnej krainie chmur i kontenerów. W rezultacie stanowi teraz przeraźliwie pokraczny twór, a kolejne iteracje jego standardu dokładają coraz to więcej bagażu do i tak pokaźnego już zapasu i tylko patrzeć pod którym źdźbłem słomy w końcu się załamie wielbłądzi kręgosłup. Doprawdy współczuję ludziom, którzy teraz, w dzisiejszych czasach, próbują się uczyć C++. Ja przynajmniej uczyłem się w latach C++98, gdy jeszcze nie był to równie pokraczny język-mutant (jak w starym dowcipie - jest to ośmiornica powstała z psa, któremu przyszyto cztery dodatkowe nogi). Żeby nie było, już wtedy to była kobyła, a porządna książka miała gdzieś z 700 stron, a jeszcze trzeba było przeczytać starego dobrego Meyersa od dobrych praktyk. Potem zaś po prostu śledząc kolejne wersje dało się jako tako nadążyć. Ale uczyć się teraz tego od zera - istny koszmar. Język ten niemożliwie opuchł w nowe możliwości, wprowadzając przy tym cały szereg nowych sposobów na strzelenie sobie w stopę i oderwanie całej nogi.

Problemem jest tu poniekąd sama metoda rozwoju języka: komitet standaryzacyjny, który po długich obradach i pisaniu szeregu publikacji (bo tutaj prosty pull request po prostu nie przejdzie) w końcu osiąga kompromis, który nie zaspokaja nikogo. Do tego zdaje się, że ci panowie, niewątpliwie inteligentni, wydają się kompletnie oderwani od rzeczywistości i potrzeb przeciętnego programisty, który nie ma szans na zapamiętanie wszystkich tych dziwactw i zależności. Kolejnym problemem jest konieczność zachowania zgodności ABI (nie API - zgodności binarnej), która powoduje, że nie idzie poprawić czegoś raz wstawionego do biblioteki standardowej. Obecnie oficjalne zalecenia w stosunku do wyrażeń regularnych w C++ to - nie używaj tych ze std. Są po prostu zbyt badziewne, wolne w działaniu, wolne w kompilacji. Analogicznie kontenery haszujące (unordered_map i unordered_set) - są tak wolne, że próba ich użycia jako jakiegoś cache'u wywołań funkcji przeważnie spowalnia program zamiast go przyśpieszyć (za dużo malloców w tle - okazuje się, że trzymając zwykły wektor jako cache można osiągnąć większą wydajność). I też się nie da wymienić, a rada jest, by korzystać z zewnętrznych bibliotek. Ale te buble jakoś nie przekonały komitetu i szykują się kolejne. Od lat tłuką się gdzieś tam na swoich konferencjach w sprawach standardowych bibliotek do zapytań HTTP, tworzenia okien i grafiki. I nie idzie dojść do porozumienia, po tak naprawdę nie idzie zmontować takiego API, żeby nie wywoływało ono narzekań. Więc może nie powinno to lądować w bibliotece standardowej, tylko trzeba by używać bibliotek zewnętrznych, tak jak danej osobie pasuje...? Może trzeba by miast tego poprawiać system modułowości, zamiast ciągle wciągać pliki nagłówkowe, jak w średniowieczu...? Ale ponieważ kompatybilność wsteczna to rzecz święta, więc oznacza to też, że zarówno stare #include jak i nowe moduły będą sobie współżyć i biedny szary koder będzie musiał (powinien) nauczyć się obu, by ich umiejętnie stosować. A lista rzeczy, ciągle rośnie. Wspomniany Meyers odszedł ze społeczności C++, bo uznał, że już za tym bałaganem nie nadąża i doprawdy trudno mu się dziwić; na ten moment chyba nikt już nie zna języka w całości z jego wszystkimi kruczkami, dziwnymi efektami ubocznymi i udziwnieniami. Nawet Bjarne apeluje, by stosować tylko ograniczony jego podzbiór i nie wychylać się w niebezpieczne szlaki i rejony. A komitet swoje - dokłada i dokłada! Patrzę na plany kolejnych wersji i wołam: litości. Za dużo tego.

Więc C++ wydaje się być przegrany, po to powstał Rust, prawda...? Z Rustem można zaobserwować trochę podobny mechanizm jak i w innych językach - wersja na wersję rośnie jak na drożdżach i się komplikuje. Oczywiście język musi wspierać asynchroniczność - teraz jest moda na asynchroniczność, każdy język musi wspierać asynchroniczność tak jak 20-30 lat temu był wielki boom na klasy i programowanie obiektowe - więc trzeba to było jakoś wciągnąć w język i już pojawiają się głosy, że to nie to, że idzie to w złą stronę. I kto wie, może się okazać, że po pierwszym początkowym boomie wejdzie jakieś zniechęcenie i zmęczenie materiału, język zacznie się sypać na przetarciach i zaraz zastąpi go kolejny, młody, dzielny, z nowej generacji, we wiecznym pędzie zastępowania dobrego lepszym (przynajmniej teoretycznie...).

Ale nie tylko Rust, wszystko idzie naprzód, dodaje feature'ów, rośnie, pęcznieje, mutuje, puchnie. Podobnie i Pythona wyposaża się teraz we wszystko co się tylko da, asynchroniczność (oczywiście - taka moda), kontrolę typów (która IMHO oszpeciła ten język przeraźliwie) czy pattern matching (bo to inny współczesny konik, każdy język teraz musi obsługiwać wzorce, bo przecież nie będzie się pisać if-ów w nowoczesnym kodzie). Podobnie i inne języki. Na mikroblogu najnowsza wersja PHP 8, również dodała więcej rzeczy, których się trzeba będzie nauczyć, znowu książki z wprowadzeniem zgrubną i tutoriale dostaną więcej linków. PHP 8, C# 10, Java 17, ECMAScript 2021, kto da więcej, kto da wyżej? Aż w końcu język zaczyna się zapadać pod własnym ciężarem i ludzie tłumnie rzucają się na sąsiednie, świeże, młode, jeszcze nie wypaczone, jeszcze nie otłuszczone, Zigi, Nimy, Kotliny i inne młodziki, po czym cykl powtarza się od zera. Show musi trwać.

Ostatnio przymierzając się do zmiany pracy rozważam, czy nie wrócić znowu do Go - język ten celowo wręcz trzymany jest w stabilnej, ogołoconej z ozdobników postaci. Sporo rzeczy trzeba tam pisać rozwlekle, bo nie ma na każdym kroku składniowej insuliny dla programistycznych cukrzyków. I chwała mu za to.

0

@Spearhead Podobnym tokiem idzie JS i czuję się podobnie w stosunku do JSa - też współczuję tym, którzy się teraz muszą go uczyć od zera (ja przynajmniej widziałem na własne oczy, jak pewne rzeczy są dodawane do języka, więc mam rys historyczny. Jak język wyglądał wcześniej i co później dodali). Tylko, że do JSa nie ma dobrej alternatywy - TypeScript tylko dodaje jeszcze więcej ficzerów (nie zdziwiłbym się, gdyby w przyszłości ficzery z TS były po prostu dodane oficjalnie do JSa). TS dla JSa to trochę jak C++ dla C.

Są też języki niszowe kompilujące się do JSa (np.Reason/Rescript, Elm), ale są mało popularne (a jak coś jest mało popularne, to jest ryzyko, że za kilka lat tego nikt nie będzie używać. Tak jak było z CoffeeScriptem).

Są też języki kompilowane do WebAssembly (np. Rust), ale znowu, dodatkowy etap kompilacji (może to u mnie, ale proste programy Rust się potrafią ileś sekund kompilować), więcej zachodu, żeby to zintegrować z przeglądarką itp.

0

Czy zrywanie z wsteczną kompatybilnością jest lepszym rozwiązaniem niż pchanie nowych ficzerów w język?

1
doskanoness napisał(a):

Czy zrywanie z wsteczną kompatybilnością jest lepszym rozwiązaniem niż pchanie nowych ficzerów w język?

Pytanie niesamowicie filozoficzne i przypadkowa odpowiedź po roku:
Zobacz sobie realne przypadki zmiany składni

  • Scala 2 -> Scala 3 (przejście trwa i jest robione maksymalnie łagodnie)
  • Python 2 -> Python 3 (chyba już zakończone przejście)
  • Perl 5 -> Perl 6 (przejście nie nastąpiło, ale Perl 6 dał poczatek nowemu językowi programowania)
  • TCL 7 -> TCL 8 (zmiana składni zabiła język)

1 użytkowników online, w tym zalogowanych: 0, gości: 1