golang

Закономерности развития языков программирования

  • вторник, 11 февраля 2025 г. в 00:00:09
https://habr.com/ru/articles/881188/

В дизайне и эволюции разных языков программирования наблюдаются одни и те же шаблоны, но почему-то редко описываются идеи за ними стоящие, из-за чего возникают вопросы почему сделано так, а не иначе. Тут я обозреваю идеи: "программисту нельзя доверять" и "язык должен быть удобен для пользователя", "каким качеством должен обладать язык программирования, чтобы заменить другой?"

с небольшими примерами на C++, Go, Swift.

Программисту нельзя доверять

Все начиналось с С, с его девизом: "Доверяй программисту", на данный момент всем понятно, что это было ошибкой. Диапазон решений, сильно отличаются, от Rust, на котором не просто довести программу до компилябельного состояния, до Python. Если посмотреть глобально, то со времен ассемблера, со временем у программиста все более и более уменьшается множество возможных программ(сначала у нас отобрали регистры, потом goto, потом самостоятельное управление памятью), которые он может написать, и это хорошо, потому что ограничения по большей части уменьшают возможность написания плохих программ.

Может показаться, что чем больше язык дает возможностей, тем он лучше, но это не так, на самом деле чем меньше в языке возможностей, тем лучше. Как правила, ограничения делают нас сильнее, например запреты на убийство и употребление наркотиков сделало все человечество сильнее.

Эта тенденция продолжится и дальше, а новый язык программирования будет изобретен в тот момент, когда определят, что же еще мешает программистам писать бизнес логику.

Язык должен быть удобен для пользователя

Достаточно простоя идея, но раскрывающаяся по-разному.

Чем меньше нажатий клавишь - тем лучше

Время программистов стоит денег(видимо это знали всегда, поэтому begin/end из Паскаля совершенно не прижились, а С получил сильный буст из-за своей синтаксической простоты). Пожалуй, максимально это понимали при дизайне Go, поэтому имеем

type Some int /*вместо*/ type Some = int;
i := 0 /*вместо*/ var i = 0;
import f "fmt" /*вместо*/ import "fmt" as f
for i := 0; i < 10; i++ /*вместо*/ for (i := 0; i < 10; i++)

Все больше встроенных функций и классов

При дизайне С++, Страуструп считал, что пользовательские типы, должны быть также удобны как встроенные в язык. Поэтому в C++ можно переопределить operator[] и operator, . Но я считаю, что Страуструп ошибался и между пользовательскими типами и встроенными целая пропасть. И если посмотреть на развитие языков, то все больше и больше в них встроенных функций и классов. В Swift встроен optional(значение, которого может не быть)

func convertToInt(_ str: String) -> Int? {
    return Int(str) // Преобразует строку в Int, но может вернуть nil
}

let optionalNumber: Int? = convertToInt("42") // Опциональное значение

// Безопасное извлечение через optional binding
if let unwrappedNumber = optionalNumber {
    print("Извлечённое значение: \(unwrappedNumber)")
} else {
    print("Значение отсутствует (nil)")
}

// Принудительное извлечение (!), если точно уверены, что значение не nil
if optionalNumber != nil {
    let forcedUnwrappedNumber: Int = optionalNumber!
    print("Принудительно извлечённое значение: \(forcedUnwrappedNumber)")
}

в go chan(по сути многопоточная очередь)

func worker(ch chan string) {
	time.Sleep(2 * time.Second) // Имитация работы
	ch <- "Работа завершена!"   // Отправка данных в канал
}

func main() {
	ch := make(chan string) // Создаём канал для передачи строк

	go worker(ch) // Запускаем горутину

	fmt.Println("Ожидание результата...")

	result := <-ch // Блокирующее чтение из канала
	fmt.Println(result)
}

Синтаксический сахар важен

Когда-то давно было предложение о том, чтобы добавить в C++ именованные параметры.

func(first: 0, second: 1);

Но их не добавили, аргументирую это тем, что это и так можно реализовать, например через дополнительные методы у классов и все работает без этого, но они ошиблись, синтаксический сахар очень важен, важно чтобы людям было легко и удобно писать хороший код. Или например лямбды — чистой воды синтаксический сахар, но как же к стати он бывает.

const auto it = std::find(vec.begin(), vec.end(), [](auto el) { return el > 0; });

Мета наблюдения

Развитие языка программирования предопределено его философией(и людьми, которым эта философия важна)

Идеи захватывают сознание людей. Поэтому никто не делает бенчмарки C++ против Go, но C++ против Rust - очень много(потому что производительность для Go лишь одна из важных идей, а для C++ и Rust - все).

В C++ всегда самой важной идеей было "абстракция с нулевой стоимостью", чтобы нельзя было переписать программу на C, чтобы она работала быстрее. И по итогу мы видим бесконечно компилирующиеся шаблоны, крайне ограниченная стандартная библиотека, отсутствие пакетного менеджера(ибо на скорость не влияет), да и в целом очень сложный язык с множеством тонкостей. По другую сторону баррикад Go lang, для создателей которого важнее были простота и скорость компиляции(что для бизнеса зачастую куда важнее).

Каким качеством должен обладать язык программирования, чтобы заменить другой?

Не хочется попадать в ошибку выжившего с утверждением, что простота и синтаксический сахар делает языки востребованными, на самом деле нет, только принципиальное отличие языка даёт ему возможность на жизнь.

Действительно, в какой момент новый язык программирования может заменить предыдущий? Как метко подметил Страуструп в "Дизайн и эволюция С++", только в случае больших отличий, новый язык может потеснить старый. И если держать это в голове, то многое становится понятным.

Почему D не заменил C++? Потому что он лишь немного лучше С++, но это улучшение не сравнимо с затратами бизнеса и отдельных людей для освоения нового языка. И с другой стороны Java сильно отличается от C++ своим ООП и сборкой мусора. Go отличается от Java ориентацией на разработку серверного ПО, простотой, встроенными корутинами и другим, Rust обеспечивает качественно иной уровень безопасности при разработке, и обещает быть столь быстрым как С++, а Python просто интерпретируемый.

feel free to comment