GoSuda

Porównanie projektów języków w środowiskach generowania kodu przez LLM: koszt abstrakcji a jawność języka Go

By Rabbit Princess views ...

Języki nowoczesne

Języki nowoczesne to języki, które w porównaniu z językami dawniejszymi zapewniają wyższą produktywność, stabilność, bezpieczeństwo i łatwość utrzymania. Języki te aktywnie wprowadzają najnowsze techniki i koncepcje oraz oferują programistom bardziej wydajne i bezpieczne narzędzia deweloperskie. Do reprezentatywnych języków nowoczesnych należą Java, Rust, Python, TypeScript i inne, a charakteryzują się one następującymi cechami.

1. Projektowanie obiektowe ( OOP )

Języki nowoczesne są w większości zaprojektowane w oparciu o koncepcje programowania obiektowego. Programowanie obiektowe wspiera zasady takie jak enkapsulacja, dziedziczenie i polimorfizm, a dzieląc złożony system na niewielkie jednostki w postaci obiektów, pozwala zarządzać nim w sposób, który czyni strukturę programu jasną i elastyczną. Jest to szczególnie skuteczne przy rozwoju oprogramowania na dużą skalę, ponieważ pozwala kontrolować koszty i nakład pracy wytwórczej oraz ogranicza problemy, które mogą pojawić się podczas utrzymania.

2. Cukier składniowy i wyrażenia

Języki nowoczesne oferują różnorodny cukier składniowy oraz składnię opartą na wyrażeniach, mające na celu zwiększenie czytelności kodu i produktywności programistów. Operator trójargumentowy, wyrażenia lambda, dopasowanie wzorców i podobne funkcje redukują kod szablonowy (boilerplate), sprawiają, że kod staje się przewidywalny, i podnoszą produktywność dewelopera.

3. System modułów

Języki nowoczesne, dzięki systemowi modułów, umożliwiają podział programu na wiele jednostek i zarządzanie nimi. Ułatwia to ponowne wykorzystanie kodu oraz zarządzanie zależnościami, a także sprawia, że utrzymanie projektu pozostaje wygodne nawet wtedy, gdy jego skala znacznie rośnie. Typowymi przykładami są Maven/Gradle w Javie, Cargo w Rust oraz npm/yarn w TypeScript.

4. Język pośredni i maszyny wirtualne

Języki nowoczesne wprowadzają język pośredni oraz maszyny wirtualne w celu zapewnienia niezależności od platformy, optymalizacji wydajności i bezpieczeństwa. Reprezentatywne przykłady to JVM, LLVM, WASM, GraalVM i inne.

Rozwój w erze AI oraz ograniczenia języków nowoczesnych

Języki nowoczesne powstały w założeniu, że większość kodu pisze człowiek. W naturalny sposób celem języków nowoczesnych jest ograniczanie powtarzalnej pracy, zwiększanie produktywności oraz tworzenie struktur umożliwiających współpracę programistów, dzięki czemu można skutecznie realizować rozległe oprogramowanie. Jednak w erze AI to założenie stopniowo traci na znaczeniu. Narzędzia oparte na AI, takie jak Copilot i Cody, automatyzują dużą część pisania kodu, a ilość kodu, jaką może wytworzyć pojedynczy programista, gwałtownie rośnie. W rezultacie cechy języków nowoczesnych, które dawniej postrzegano jako zalety, stopniowo przekształcają się w przestarzałe wady.

Projektowanie obiektowe

AI może poprawnie i szybko analizować strukturę, w której cała informacja jest jawnie zawarta w pojedynczej funkcji lub module. Natomiast wraz ze wzrostem kontekstu rośnie liczba obszarów wymagających wnioskowania, co obniża produktywność i dokładność AI. Kod napisany w OOP rozprasza logikę po wielu obiektach zamiast gromadzić ją w jednym miejscu, co w rezultacie wymaga od AI większego kontekstu. Przyjrzyjmy się poniższemu kodowi.

 1public class AnimalHandler {
 2    public void handle(Animal animal) {
 3        animal.speak();
 4    }
 5}
 6
 7public class Main {
 8    public static void main(String[] args) {
 9        AnimalHandler handler = new AnimalHandler();
10
11        Animal a1 = new Dog();
12        Animal a2 = new Cat();
13        Animal a3 = new Horse();
14
15        handler.handle(a1);
16        handler.handle(a2);
17        handler.handle(a3);
18    }
19}

W powyższym kodzie, aby ustalić, jakie działanie faktycznie wykona metoda speak(), AI musi powtarzać następujące wnioskowania:

  1. Instancją jakiej konkretnej klasy jest animal?
  2. Gdzie zdefiniowana jest nadpisana metoda speak() w tej klasie?
  3. Gdzie znajdują się definicje klas Dog i Cat oraz jakie działania zawierają?
  4. Czy istnieje możliwość, że klasy Dog i Cat zostaną nadpisane w innych klasach?

Informacje te nie są zgromadzone w jednym pliku ani w jednej funkcji, lecz można je zrozumieć wyłącznie, śledząc relacje między plikami klas oraz strukturę dziedziczenia. Co więcej, elementy określane w czasie wykonania, takie jak refleksja czy ładowanie dynamiczne, są z perspektywy AI jak czarna skrzynka, przez co ich analiza jest praktycznie niemożliwa.

Cukier składniowy i wyrażenia

AI preferuje jawne zachowania nad ukrytymi oraz woli łączyć proste struktury w jedną poprawną odpowiedź, zamiast korzystać z rozbudowanych konstrukcji. Cukier składniowy natomiast dopuszcza wiele form zapisu, przy czym ich działanie wewnętrzne bywa identyczne lub podobne. AI musi nauczyć się znaczenia każdej z tych form i może mieć trudność z określeniem, które konstrukcje należy rekomendować w danej sytuacji.

Język pośredni i maszyny wirtualne

AI uczy się przede wszystkim na kodzie źródłowym. Natomiast język pośredni (bytecode) w procesie kompilacji traci znaczenie takich elementów jak nazwy zmiennych, a jego interpretacja musi być dokonana przez maszynę wirtualną w czasie wykonania. Jest to zadanie, które AI bardzo trudno zrozumieć lub przekształcić. Na przykład tłumaczenie z TypeScript na Go jest dla AI możliwe, natomiast przekształcenie bytecode'u silnika V8 na bytecode JVM lub kod maszynowy jest praktycznie niemożliwe.

Prawdziwie nowoczesny język w erze AI

Na podstawie powyższych rozważań możemy stwierdzić, że cechy języków nowoczesnych w rzeczywistości są elementami przestarzałymi, nieodpowiednimi w erze AI. Jaki język może więc stać się najbardziej odpowiednim językiem nowoczesnym w erze AI? Wskazówek można szukać w elementach, które dawne języki nowoczesne odrzucały. Język nowoczesny w erze AI powinien:

  1. Posiadać jawną składnię i naturalny przepływ sterowania.
  2. Odejść od nadmiernej struktury OOP i mieć płaską, prostą strukturę.
  3. Zamiast nadmiernego cukru składniowego lub ukrytych wzorców, tworzyć wiele funkcji przy użyciu ograniczonego zestawu podstawowych słów kluczowych.
  4. Posiadać prosty i przewidywalny system budowania, a nie złożony.

Językiem, który najlepiej spełnia te kryteria, jest Golang. Język Go został zaprojektowany tak, aby za pomocą minimalnego zestawu podstawowych słów kluczowych i prostych kombinacji składni można było zrealizować większość praktycznych funkcji, a jednocześnie konsekwentnie eliminuje zbędne abstrakcje oraz ukryte działania. Jest to struktura korzystna zarówno dla programistów, jak i dla AI, które interpretuje i generuje kod.

Taka przewidywalna i płaska struktura, jawny przepływ sterowania oraz spójna składnia, do których dąży Go, sprawiają, że język ten lepiej odpowiada środowisku programistycznemu, w którym AI aktywnie uczestniczy, w przeciwieństwie do dotychczasowych języków nowoczesnych, zaprojektowanych z myślą o człowieku. Konserwatywna filozofia projektowa Go, uważana kiedyś za przestarzałą, w erze AI może okazać się najbardziej zaawansowanym wyborem.