LINUX.ORG.RU

Что происходит с популярностью Go?

 ,


3

2

Примерно с лета прошлого года популярность языка программирования Go неуклонно падает. По крайней мере если верить индексам TIOBE и PYPL. В начале прошлого года Go входил в первую десятку самых популярных языков программирования, но сейчас он уже во второй десятке - ближе к её нижней границе. Если TIOBE и PYPL не отражают действительность, то какие другие индексы отражают её лучше? Хотя на https://langpop.com/ Go всё ещё в первой десятке. Что вообще происходит с Go и каковы ваши прогнозы на ближайшие пару лет?

Ну и чтобы два раза не вставать, дополнительный вопрос. Один из конкурентов Go - это JavaScript/TypeScript на платформе node.js. Когда популярность Go росла, значительный процент новых программистов на Go был представлен теми, кто переходил на него с JS/TS. Однако если сейчас посмотреть на объявления о работе (живу не в России), то там какое-то засилие именно JS/TS, причём в основном это молодые компании и стартапы. Можно ли это объяснить банальной экономией на найме, когда одни и те же люди делают и бэкэнд и фронтэнд? Иначе я никак не могу понять тягу к языку, в котором нет нормальной параллельности и вообще куча костылей и легаси.

Update: большой брат подслушал и порекомендовал в ютубе: www.youtube.com/shorts/Vh97uPhmhqI



Последнее исправление: hummer (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Но у жабы проблема в том, что она слишком древняя и поэтому очень костыльная в плане экосистемы. Щас бы жабу, но с экосистемой того же голанга – вот это была бы пушка!

Пушка уже есть, котлин называется. Жаба на стероидах. Вполне современная, вполне удобная, полностью совместимая с жавой. Взяли много хорошего из скалы, а много плохого брать не стали. Ну а что до экосистем – я хз чего там такого многого у голанга (syscalls? все C-либы?), но если говорить про бэк-разработку, то мне сложно представить, как можно переплюнуть по объёму жавовскую экосистему. (То, что по существенной и самой популярной доле этой экосистемы помойка плачет, – другой вопрос. Как по мне, по языку без ООП и исключений тоже помойка плачет.)

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

> языку без ООП

Но ведь в Go возможен ОО-стиль. Единственное отличие от традиционного стиля: отсутствие наследования реализации. Вы только на этом основании отрицаете наличие ОО-стиля или что-то ещё?

Что до наследования реализации, то это вовсе не неотъемлемая часть ОО-стиля. Она редко где нужна и там, где вроде бы нужна, легко заменяется на композицию. Отношение вида is-a плохо ложится на высоко-абстрактный код.

> Умный человек . . . решит, что всё хорошо в меру, и выработает и формализует для себя какие-то сбалансированные подходы. . . . А дурак будет «хватать о камни».

Смотрите, с таким же успехом можно придраться к любому. Вот написали вы некий код. И тут кто-то говорит: а почему не используешь паттерн декоратора, к примеру? Назовут дураком, скажут, что ты не понимаешь, что «всё хорошо в меру».

К любой системе, включая ЯП, применяется такая же логика. Система служит некой единой цели. Например, Go — «современный, минимальный язык с низким порогом входа» (приблизительное определение). C++ — «мощные абстракции без издержек CPU» (тоже приблизительное определение). И там, где системе нужно принять выбор какой она должна быть, решение принимается на основании того, что ближе к цели системы.

Если же всё объяснять мерилом умность/тупость, то это практически всегда даёт нам шанс назвать любую сторону тупой, поскольку мы её по-настоящему не понимаем.

«Объяснение явлений через глупость людей — это характерная черта снобов, выдающая их собственный узкий взгляд на мир.»

kaldeon ★★
()
Ответ на: комментарий от kaldeon

там, где вроде бы нужна, легко заменяется на композицию.

А какой-нибудь сишник скажет, что this можно передавать в любую функцию явно первым аргументом, и vtable руками составлять, так что этот ваш ООП вообще не нужен.

dimgel ★★★★★
()
Ответ на: комментарий от dimgel

Я не спорю с тем, что Си — не ОО-язык.

Современное определение ООП таково: инкапсуляция, наследование, полиморфизм, абстракция. Среди них главенствующие принципы: инкапсуляция и абстракция. (Си не поддерживает ни то, ни другое.) Наследование считается «столпом» только из-за того, что это была общепринятая практика в ранних ОО-языках. Другими словами, так исторически сложилось.

Причём как минимум в 1994 году люди уже поняли, что что-то не так с наследованием. Популярная книжка Design Patterns [1] уже тогда выдвинула принцип “favor composition over inheritance.” Стало быть, никакой это не «столп», если ему отведено второстепенное место в ОО-дизайне.

Поэтому Go вполне поддерживает ОО-стиль, если судить по существу.

[1]: https://en.wikipedia.org/wiki/Design_Patterns

kaldeon ★★
()
Ответ на: комментарий от dimgel

> «favor» != «ALWAYS use»

Мне не нужно доказывать, что наследование ВСЕГДА неуместно. Я этого не утверждал. (Что угодно уместно в определённом контексте, даже goto.) Я говорил о том, что это второстепенная роль в ОО-дизайне.

> Впрочем, мне надоел этот разговор.

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

kaldeon ★★
()
Ответ на: комментарий от dimgel

совместимая с жавой

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

no-such-file ★★★★★
()
Ответ на: комментарий от kaldeon

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

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

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

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

короче, все мои лично попытки на нем писать, заканчивались полным расстройством, и отскоком на что нить другое.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от no-such-file

А как только ты наступаешь в жабу, то сразу тонешь в ней по шею.

Красивый образ, но вообще-то весь страшный интерконнект с жабой в кложе делается как-то так:

;; создадим instance java.util.Date
(java.util.Date.)
#inst "2026-08-02T11:09:42.926-00:00"
;; вызовем метод класса
(.toString (java.util.Date.))
"Sun Aug 02 13:09:55 CEST 2026"
;; то же самое, но с аргументом и эквивалентным синтаксисом конструктора, просто так
(def d (new java.util.Date))
(.setYear d 1)
d
#inst "1901-08-02T12:11:49.285-00:00"

Надеюсь, никого не напугал этакими ужосами.

ugoday ★★★★★
()
Ответ на: комментарий от alysnix

Когда в этот ваш С++ мультиметоды завезут? А то на других ругаетесь, а у самих язык по фичам до сих пор уступает CLOS (более чем сорокалетней давности).

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

А то на других ругаетесь

ни в коем разе.

Когда в этот ваш С++ мультиметоды завезут?

когда вы предложите эффективную реализацию.

alysnix ★★★
()
Ответ на: комментарий от alysnix

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

Было бы интересно выслушать.

> а именно - совместимость указателей по базовому классу.

Где здесь, собственно, дизайн? Скорее zero CPU cost заморочка. Это очень удобный стандарт: можно очень многое принести в жертву.

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

Если опустить zero CPU cost, то это банальный полиморфизм. Полиморфизм — не существенная характеристика наследования. Аспект наследования реализации оказывает куда большее влияние не дизайн.

kaldeon ★★
()
Ответ на: комментарий от ugoday

Когда в этот ваш С++ мультиметоды завезут?

А в Go?

а у самих язык по фичам до сих пор уступает CLOS (более чем сорокалетней давности).

А как с этим дело обстоит у Go?

eao197 ★★★★★
()
Ответ на: комментарий от kaldeon

Где здесь, собственно, дизайн?

дизайн тут в том, что вы можете вынести работу с базовыми(общими) свойствами объектов в отдельный класс или функцию, инкапсулировать эту обработку там, и не морочится с совместимотью по указателю на эти обьекты. и все это зерокост.

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

Если опустить zero CPU cost, то это банальный полиморфизм.

полиморфизм не может быть банальным, иначе банальным можно обьявить все. это просто зерокост полиморфизм, аналогов которому еще поискать.

Аспект наследования реализации оказывает куда большее влияние не дизайн.

у кого как. любую сильную концепцию можно использовать во вред. в том ее сила. используйте на пользу.

критковать наследование за излишнюю зависимость реализаций, это все равно что критиковать вилку за то, что ею можно ткнуть в глаз.

само определение наследования не преполагает, что вы будете его использовать там, где не стоит.

если вам неохота, чтобы у вас наследники тырили реализацию из базового класса - обьявите методы приватными.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 3)
Ответ на: комментарий от kaldeon

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

А подскажите, пожалуйста, как в Go реализуется вот такой элементарный паттерн, легко доступный в классических ОО-языках:

class base {
protected: // Хотя тот факт, что это protected роли не играет.
  virtual void do_specific_action() = 0;

public:
  void do_something() {
    ... // Какие-то начальные действия.
    do_specific_action();
    ... // Какие-то финальные действия.
  }
  ...
};

void f(base & obj) {
  obj.do_something();
  ...
}

class derived_one : public base {
  void do_specific_action() override { ... }
  ...
};

class derived_one : public base {
  void do_specific_action() override { ... }
  ...
};

И в f можно передать ссылку на любого наследника от base.

eao197 ★★★★★
()
Ответ на: комментарий от alysnix

Т.е. мультиметодов нету и не предвидется? Ладно, оставим С++ в категории отсталых языков из 80-х. Вернусь к вопросу лет через 10.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

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

никому в с++ такой хоккей не нужен. даже если его туда вставят - он сто пудов будет отключаться ключиком компилятора, который будет стоять по умолчанию в «отключить».

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

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

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

мои «претензии» к голангу я же сформулировал - слишком жесткие рамки для форматирования и правила для идентификаторов. остальное терпимо. а некоторое - каналы и корутины - даже приятно.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Давайте лучше вы сформулируете свои тезисы в явном виде, чтоб мне не додумывать за вами в чём суть претензии.

ugoday ★★★★★
()
Ответ на: комментарий от alysnix

По местным правилам это не баг, это фича. Как в армии, безобразие, но единообразие. Оно, может, и претит истинному художнику, но для художеств есть Perl. А тут промышленный код, который выглядит и устроен абсолютно одинаково и предсказуемо. Можно любого среднего гошника бросить в любой средний проект и он практически сразу разберётся и начнёт приносить пользу. Ситуация, когда каждый программист выбирает и пишет на своём подмножестве языка исключена настолько, насколько это вообще возможно.

ugoday ★★★★★
()
Ответ на: комментарий от alysnix

> вы можете вынести работу с базовыми(общими) свойствами объектов в отдельный класс или функцию, инкапсулировать эту обработку там, и не морочится с совместимотью по указателю на эти обьекты. и все это зерокост.

Обещан был «элемент нормального дизайна». Нужен хотя бы один пример, который бы оправдал наличие наследования в языке. А вы объяснили общий принцип работы, справедливый в любом случае использования.

Это не опровергает тезис, что «[наследованию] отведено второстепенное место в ОО-дизайне». ОО-дизайн — это один шаг выше механики, про поиск лучших (а не любых) методов решения проблем.

> как раз зерокост решения . . . и есть самые ценные

Не для всех и не для любой цели.

> полиморфизм не может быть банальным

Я не называл полиморфизм банальным. Я указал, что полиморфизм *сам по себе* — не существенная характеристика наследования как понятия.

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

И именно этот аспект является самым проблематичным с точки зрения ОО-дизайна. Он ослабляет инкапсуляцию [sny86].

> используйте на пользу.

Наследование позволяет легче изменить переиспользуемую реализацию. Вот и вся польза.

В контексте ОО-дизайна это ничтожная польза, несоизмеримо маленькая с масштабом сложностей в эксплуатации.

> если вам неохота, чтобы у вас наследники тырили реализацию из базового класса . . .

Дело не в желании. Воровство реализации — это часть определения наследования. В том виде, в котором наследование существует в C++ или Java, формально оно называется «наследование *класса*». Реализация — это часть класса. Наследование без реализации — это и есть использование инструмента не по назначению.

> . . . обьявите методы приватными.

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

Это оставит нас с чистым наследованием *типа*. Это позволяет нам аккуратно выразить отношения вида is-a. Проблема только в том, что это редко нужно и когда нужно, то правильным инструментом является *подтип*, а не наследование класса.

[sny86]: Alan Snyder. Encapsulation and inheritance in object-oriented languages.

kaldeon ★★
()
Ответ на: комментарий от eao197

как в Go реализуется вот такой элементарный паттерн

Напрямую — никак, ибо наследование реализации отсутствует. Это, кстати, защищает от проблемы хрупкого базового класса [1].

Но вообще аналогичный код пишется так:

1. Пишем derived1 и derived2

2. Пишем base, который при создании принимает derived1 или derived2 через общий интерфейс. Именно к нему будем обращаться в base.DoSomething.

3. В f передаём только инстанс base

В целом, прямое применение принципа “favor composition over inheritance.”

Важно и то, что интерфейс привязан только к base. Это расслабляет порядок написания: можно сначала реализовать derived1/derived2 и уже потом написать base/doer.

Код выглядит как-то так:

type doer interface{ Do() }

type base struct{ doer doer }

type derived1 struct{}

type derived2 struct{}

func (b *base) DoSomething() {
	b.doer.Do()
}

func (d *derived1) Do() { fmt.Println("hello world") }

func (d *derived2) Do() { fmt.Println("hello existence") }

func main() {
	b := &base{
		doer: &derived2{},
	}
	b.DoSomething()
}

[1]: https://en.wikipedia.org/wiki/Fragile_base_class

kaldeon ★★
()
Последнее исправление: kaldeon (всего исправлений: 2)
Ответ на: комментарий от kaldeon

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

нет там никакого «наследования реализации» по дефолту. наследование это в чистом виде отношение - «is a» композиция - отношение «consists of»

и второе никак не заменяет первое. потому что автомобиль есть «средство передвижения», а «consists of» - мотор и 4 колеса.

если вы инкапсулируете «средство передвижения» в автомобиль - у вас просто неверно устроенный класс. ибо автомобиль не может состоять из «средства передвижения».

Наследование без реализации — это и есть использование инструмента не по назначению

аналогично вы не можете отнаследовать автомобиль от мотора и 4 колес. и именно такую дурь обычно и эксплуатируют в своих аргументах критики «наследования».

у вас нет способа коректно отразить is_a, усли у вас нет наследования хоть тушкой, хоть чучелом.

если вам в разработке такое отношение is_a - даром не надо, то пробуйте без воды и костылей спроктировать библиотеку для гуя, где вся ветвистая иерархия растет от базового класса window или типа того. вам по-любому придется делать квазинаследование через задний проход, ну или виде «наследования» интерфейсов.

Наследование без реализации — это и есть использование инструмента не по назначению

в кондовом ооп можно наследовать хоть с реализацией, хоть без.

class Base {
public:
  void fun1();
  Base();
}

или так

class HiddenBase;
class Base {
  HiddenBase* _hidden;
public:
  void fun1();
  Base();
}

если у вас есть общие свойства, то они должны быть где-то реализованы.

у вас два выхода. или написать в базовом классе такую реализацию и закрыть вопрос(забив на критиков наследования). или написать много одинаковых реализаций в разных местах и поиметь головную боль. или написать одну, но вызывать ее в разных классах. что есть симуляция наследования через тавтологию и самоистязяние.

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от kaldeon
> type doer interface{ Do() }
> 
> type base struct{ doer doer }
> 
> type derived1 struct{}
> 
> type derived2 struct{}
> 
> func (b *base) DoSomething() {
>   b.doer.Do()
> }
> 
> func (d *derived1) Do() { fmt.Println("hello world") }
> 
> func (d *derived2) Do() { fmt.Println("hello existence") }
> 
> func main() {
>   b := &base{
> 	doer: &derived2{},
>   }
> 
> 	b.DoSomething()
> 
> }

что по простому пишется так

class Base {
  virtual void Do();
}

void DoSomething(Base& b) {
   b.Do()
}

class Derived1: public Base {
  void Do() override {fmt.Println("hello world")}
}

class Derived2: public Base {
  void Do() override {fmt.Println("hello existence")}
}

void main() {
  Derived2 b;
  DoSomething(b);
}

отличия очевидны. первый код - кишки наружу. есть просто данные классов, сами по себе, и байндинги к этим данным, которые тоже раскиданы по файлу сами по себе. чтобы понять - какие методы данный «класс» реализует - надо просмотреть весь файл в три тыщи строк. опять же - квазивиртуальный метод Do реализован ручками - через хранение в base указателя на реальную функцию. который потом инитится опять же ручками при создании обьекта. такая вот квази VMT прямо в обьекте. а что если таких методов - десяток?

короче в такой манере можно и на си написать.

второй код - все классы описаны целостно, посмотрел и все понял. Никакой ручной VMT нет. пиши хоть сто виртуальных методов. это и называется ооп

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от kaldeon

Напрямую — никак, ибо наследование реализации отсутствует.

ИМХО, это полностью опровергает ваш тезис о том, что наследование – это «Это абстрактное понятие и судить его нужно именно как абстрактное понятие». Поскольку есть вполне себе конкретные примеры кода, которые демонстрируют наличие или отсутствие этого самого «наследования».

Это, кстати, защищает от проблемы хрупкого базового класса [1].

Если вы эмулируете ООП вручную, то вы получаете те же самые «проблемы», но еще и обмазанные говном этой самой ручной реализации.

Впрочем, опыт (да и аргументы в этом треде) показывает, что Go-шники с удовольствием жрут говно с двух рук и еще и с радостью доказывают, что все правильно, что так и надо.

eao197 ★★★★★
()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)