Comments 78
В Go все таки есть обобщенное программирование и это interface{}
Опять же это непривычно, т.к. нет синтаксического сахара, как в Java.
Эм, вы правда не знаете, что обобщенное программирование — это не синтаксический сахар, а interface{}
— это не обобщенное программирование?
оставлю тут ссылочку
ArrayList<String> list = new ArrayList<>();
конструкция для программиста, но для JVM это:
ArrayList<Object> list = new ArrayList<>();
так что ничем иным, как синтаксическим сахаром это назвать нельзя
То, что в Java это так, совершенно не означает, что в других языках это так же. В частности, в каком-нибудь банальном С# List<int>
— это именно List<int>
, и никаких кастов (и никакого боксинга/анбоксинга, связанного с кастами) в рантайме не происходит.
Вы говорили, что в Go есть обобщенное программирование — на основании того, что оно реализовано "как в Java, но без синтаксического сахара". Проблема в том, что то, что осталось (а именно — касты всего и вся к/из interface{}
, он же object
, он же void *
) — это не обобщенное программирование.
Про interface{} — это слова создателей языка, не мои
Пока что в факе про язык написано, что дженериков в нем нет. Так что я не уверен, что вы правильно понимаете "слова создателей языка".
Основная мысль была такова, что если мы объявляем списки или карты, которые используют интерфейс(Identifier, не interface{}), то в такие структуры данных мы можем положить любой тип, реализующий этот интерфейс
Мне кажется, вы упускаете из виду тот факт, что списки и словари в Go — как раз дженерики (да, вот так непоследовательно). И именно поэтому вы можете создать список объектов любого типа, вместо необходимости объявлять новый тип списка для каждого типа содержимого. Но попробуйте теперь создать свою собственную коллекцию для произвольного типа (в качестве чисто академического упражнения).
Так что я не уверен, что вы правильно понимаете «слова создателей языка»
статья ребят из яндекса. В заключении статьи написано, про дженерики. Говорю же, не я это придумал)))
Мне кажется, вы упускаете из виду тот факт
Полностью с вами согласен — списки и словари дженерики. Я говорю про другое:
template <typename T> bool eq(T x, T y)
в Go
type Equaler interface {
IsEq(Equaler) bool
}
func Eq(x, y Equaler) bool
type IntEqualer int
type StringEqualer string
// и т.д.
статья ребят из яндекса. В заключении статьи написано, что дженерики.
Правда? Цитата:
То, чего не хватает в первую очередь мне — это generics. [...] Действительно, какой-то generic code можно писать с использованием интерфейсов, но в части случаев этого не хватает.
Я говорю про другое
Угу. Попробуйте с помощью этого "другого" реализовать банальную монаду Maybe (она же Option[T]
). Естественно, для всех T
. Или, оставаясь в пределах вашего примера, описать следующее: нам нужен метод Sort<T>(T[] data, Comparer<T> comparer)
, который принимает на вход массив (ура, они уже дженеричные) и компаратор (который бы работал с тем же типом, которого массив).
Sort(T[] data, Comparer comparer)
я думаю, что такой же гибкости в go не добиться, увы. Но все же некую обобщенную реализацию можно написать. Эта интересная тема для статьи, может накидаете еще пару примеров или ссылок?
я думаю, что такой же гибкости в go не добиться, увы
Гибкость тут ни при чем. Это просто наглядная демонстрация, зачем бывают нужны дженерики помимо навязших всем в зубах контейнеров.
Но все же некую обобщенную реализацию можно написать.
Без кастов? Я бы с любопытством посмотрел на это.
Это просто наглядная демонстрация, зачем бывают нужны дженерики
Я вас прекрасно понял! Я пытался на простом примере с Equaler показать, что это возможно в принципе. Т.е. в функции и методы структур и сами структуры в go могут работать с интерфейсами, а не с конкретными типами данных.
Без кастов? Я бы с любопытством посмотрел на это.
Нужна пища для размышления…
Я вас прекрасно понял! Я пытался на простом примере с Equaler показать, что это возможно в принципе. Т.е. в функции и методы структур и сами структуры в go могут работать с интерфейсами, а не с конкретными типами данных.
Вы путаете обобщенный код с полиморфизмом. Возможность работать с интерфейсами — это полиморфизм, он с обобщенным (generic) кодом ничего общего не имеет. А обобщенный код — это возможность "типизовать" конкретный фрагмент кода (метод, класс, интерфейс, бла-бла) конкретным типом, известным только потребителю (и при этом, понятное дело, сохранить статическую типизацию).
Это уже проблемы явы, но если языки поддерживают мономорфизацию, то этот сахар превращается в уже очень мощную фичу без накладных расходов.
>Все было бы круто, если не JVM. Для того, чтобы развернуть серьёзное приложение на Java, вам понадобится тачка с большим количеством оперативной памяти. И это совершенно не годится для спартапов.
А потом рассказывается, как можно написать кучу кода, для замены JVM.
А где собственно подтверждение причины?
Где доказательства, что памяти действительно надо во столько раз больше, что дешевле перейти на Go?
Почему писать больше кода в Go дешевле, чем добавить памяти на сервак (это не учитывая, что доказательство большего потребления памяти вами не приведено)?
второе, достаточно запустить любой сервер приложений Java и посмотреть количество потребляемой памяти. Недавно мы тестили Go и Java(spring + jetty). Go скушал 37мб, против 678мб у Java, да производительность показал в два раза выше. Мы списали это на spring.
Вот так и надо было писать в статье «Лично я не смог хоть немного выучить Java и сделал приложение которое потребляло много ресурсов», а не громкие завления в духе «JVM жрет много ресурсов и точка»
Ну и опять же, Давно под Go есть шаред хостинги по 1$?
Ибо у меня несколько проектов на Java и Scala крутится на 5$ инстансах DO.
При чем ДО не самый дешевый, аналогичные инстансы можно найти сильно дешевле.
>второе, достаточно запустить любой сервер приложений Java и посмотреть количество потребляемой памяти
А зачем его запускать-то? Ну давайте напишем приложение на Go, которое отожрет много памяти и будем его в пример приводить… Да, кстати, а есть они вообще под Go эти самые сервера приложений? А то вы сравниваеете навороченный програмный комплекс с простым web приложением, что как бы косвенно говорит о вашей квалификации как разработчика и о достоверности ваших выводов.
>Недавно мы тестили Go и Java(spring + jetty)…
А недавно мы написали кривое приложение на Go и оно съело все ресурсы… и что с того?
Это лично вы не умеете писать на Java, а не Java потребляет много ресурсов.
Опять пример не в пользу вашей квалификации и сделанных выводов.
Чтобы писать статьи с такими громкими заявлениями, надо для начала хорошо разбираться в вопросе.
Если уж беретесь что-то сравнивать в таком ключе, то потрудитесь запастись реальными примерами и корректными сравнениями, а не домыслами вытекающими из вашей слабой квалификации.
Для начала, далеко не всем нужны сервера приложений. Если вы на гласфише пытались делать публичный веб проект, то вы должны были четко понимать для чего вы это делаете. А так по вашим каментам все выглдяит так, буд-то вы по хреновым хеловордам из инета пытались на Java поднять проект и он у вас тормозил.
Не разобравшись начали писать на Go. К сожалению, такими «саксес стори» пестрит весь интернет…
Я не противник Go, и не адвокат Java.
Просто надо корректно сравнивать технологии, а не выдавать собственную некомпетентность за плюсы и минусы технлогий. Если это ваш личный опыт, то не делайте обобщений, так и пишите «мы не смогли», «мы не умели», «мы не знали как, поэтому взяли первое попавшееся решение в интенете» и т.д.
Тогда ваша статья будет иметь правильную тональность.
Не «JVM говно, а Go молодец»,
а «Начинающему разработчику, проще начать с Go, т.к. язык молодой и все решения свежие в гугле проще находятся. А Java уже 20 лет и при поиске надо фильтровать старые не эффективные решения от новых более эффективных»
P.S. Ну и да, ждем пруфов в виде одинаковых проектов, в которых Go есть памяти в 18 раз меньше чем Java.
Чтобы можно было понять, на сколько адекватно все здесь вами написанное.
я не смог хоть немного выучить Java
А так по вашим каментам все выглдяит так, буд-то вы по хреновым хеловордам из инета пытались на Java поднять проект и он у вас тормозил.
взял офф доки java ee, по ним делал. Проект не тормозил, просто много кушал. Я не противник java, просто мне не подошло.
А недавно мы написали кривое приложение на Go
писал не я, а коллега java программист, не в его интересах было писать криво.
Не «JVM говно, а Go молодец»,
я этого и не говорил, и вообще статья о проблемах go, а не jvm))
А Java уже 20 лет
круто! Но это не означает, что это панацея для всех проектов
Ну и да, ждем пруфов в виде одинаковых проектов, в которых Go есть памяти в 18 раз меньше чем Java
легко, достаточно написать сервер на go и и на spring, но повторюсь статья не об этом))) извините, если задел за живое)
Немного оффтоп, но я так и не смог нормально ограничить heap руби программе, в отличие от явы. Может, я конечно плохо старался, но я думал, такие вещи должны быть очевидными.
А в плане потребления памяти — как правило, любая VM будет есть сколько дадут, т.к. это позволяет реже собирать мусор. Руби тоже жрет пока может, если объекты интенсивно создаешь.
Веб-приложение кушает 3640 килобайт.
![image](https://habrastorage.org/files/9f9/91f/97e/9f991f97eacf4664bf77d80f76e57123.png)
Для того, чтобы развернуть серьёзное приложение на Java, вам понадобится тачка с большим количеством оперативной памяти. И это совершенно не годится для спартапов.
Что за бред. У хецнера 60 евро в месяц за Xeon® E3-1275 v5 и 64 гигабайта DDR4 оперативки.
Выходит, по мнению автора, 60 евро в месяц это просто неподъемная сумма для стартапа.
Ну вот на таком сервере ваших приложений можно запустить 32 штуки. Итого чуть меньше двух евро в месяц за приложение. Что у вас за сервис такой, что два евро в месяц найти негде?
func Foo(bar int = 5)
Это можно обойти через динамические аргументы (args ...int), но дико неудобно.сравнение go с его главным конкурентом по потреблению ресурсов — nodejs.
Судя по опыту миграции некоторых компаний с nodejs на go (Digg, VK), нода конкурентом не является.
Но как-то скучно без неё (Кати) на хабре стало. ))
А пока из консоли браузера выполните
$("#comment_8873074").hide();
дабы не видеть это безобразие xD
Особенно когда часто переключаешься с js и обратно.
В общем, мои скромные изыскания в части сравнения реализации процессов и способов обмена данными между ними, выявили следующее:
1. Обмен сообщениями.
В Go для обмена сообщениями используются каналы. Канал явно существует, как понятие. Он передается в функцию (горутину), а та уже с ним работает.
Что понравилось:
— вроде бы можно одно сообщение из канала принять двумя корутинами
Что не понравилось:
— необходимость самого существования такого понятия, как канал (в ерланг это не надо)
— при отправке сообщения в канал, отправляющий процесс «зависает» пока принимающий не примет данные. Для чего такое может быть нужно, мне сложно понять. В итоге, может выстроиться цепочка из процессов от соединения с пользователем, до записи на диск, и вся она «зависнет» на работе с файловой системой. Но в принципе, на сколько мне сумели объяснить, в таких случаях сообщения «принимают и складывают на стек», читай принимают и складывают в память процесса. Чтобы устранить зависания, в канале может быть включен буфер ФИФО, это от части решает проблему с зависанием.
В Erlang для обмена сообщениями между процессами, не используется понятие канала. Нужно знать PID процесса и отправить ему сообщение. Отправляющий процесс не зависает при отправке сообщения. Он его просто отправляет и работает себе дальше. Отправленное сообщение упадет в почтовый ящик процесса, в очередь, и тот его считает тогда, когда ему будет нужно. Но процесс читающий сообщение из почтового ящика «зависнет», если там нет сообщения. Но это я считаю нормально (нет данных для дальнейшей работы — ложимся спать). К тому же есть возможность установить таймаут и после него выполнять работу дальше. Если же нужно отправить сообщение синхронно, с подтверждением приема от удаленного процесса, то мы его отправляем, потом читаем входящие — в этот момент мы «зависнем», пока не придет ответ. То есть мы можем выбирать режим (почти аналогия буфера каналов).
Вопрос того, что в Erlang процесс обмена сообщениями внутри ноды или кластера происходит прозрачно, тут не обсуждаем. Приложения Go в кластер не объединяются. Там своя тема под названием микросервисы и то, как они меж собой связаны уже решает разработчик, но пусть так. Из положения выходят с помощью grpc или как-то иначе. Но для меня до сих пор остается загадкой, как множество процессов одного приложения, например tcp соединений с клиентами, должны обмениваться с процессом, например, базы данных? Сколько будет каналов, как их столько держать и обрабатывать? В Erlang даже вот чисто концептуально все это получается менее нагроможденным и простым. Процесс клиент просто отправит сообщение серверу, в сообщении будет PID, куда слать ответ. Сервер процесс не хранит всю связность с процессами — клиентами. Очередность будет соблюдена благодаря тому, что сообщения в почтовом ящике процесса выстраиваются в очередь. В то же время, есть возможность выбрать сообщения в другом порядке.
2. Зеленые процессы. В Erlang всё понятно — это виртуальная машина, она выполняет байткод, каждый процесс выполняется на заданное количество редукций, разрешенных для него, потом, что бы он не делал (за исключением атомарных операций), он будет прерван и выполняться будет уже другой процесс. Распределяет работу шедулер. Из коробки ноду Erlang можно считать целой ОС мягкого реального времени.
Я грешным делом сначала подумал и возрадовался, что в Go для реализации работы горутин используется что-то типа встроенной виртуальной машины. Это было бы шикарно: виртуальная машина в приложении. Но в процессе общения в Go'шном канале (ребятам, кстати, спасибо), выяснилось, что никакой виртуальной машины нет. Но как же работают горутины в параллельном режиме? Как осуществляется шедулинг? Оказалось, что если создать горутину, которая в бесконечном цикле прибавляет единицу и никуда это не передает, то она «зависнет» насовсем. И если запустить Go приложение в один процесс (а не по умолчанию 6 — 8), то приложение зависнет насовсем с момента запуска горутины и то, что будет после этого, никогда не выполнится! Я даже сделал ошибочный вывод о том, что на одноядерном процессоре это все в принципе может перестать работать. Но потом вспомнилось про системные threads и что они существуют как бы параллельно даже на одном ядре. Так что, приложение Go вполне себе честно виснет, будучи запущенным в один процесс, если горутина зациклена. Открытием было то, что передача управления на другие горутины происходит при вызове функций других модулей (просто, или, к примеру, вывода на экран). То есть, ничего общего с равномерным распределением ресурсов тут получается, что и нет. Каждый процесс может неопределенное время монопольно использовать один thread, пока не отпустит. В условиях того, что thread'ов выделяется несколько, то это бросается в глаза как бы не сразу. Все это напоминает многозадачность Win95, когда задачи сами решали, когда отдать управление ОС. На сколько мне известно, решение о том, какую горутину выполнять дальше, решает рандом. И вообще это еще в процессе разработки, потому что этот алгоритм показал плохие результаты. Другие статьи по Go вполне ожидаемо сообщают о том, что не всегда целесообразно создавать много параллельных горутин.
3. Производительность. Перед тем, как сделать простой веб сервер на Go и на Erlang и сравнить их по производительности, я взял задачу, которая гарантировано удобнее для Go. Это счастливые билетики. Тупо в лоб 6 вложенных циклов от 0 до 9 и миллион итераций сравнения. Естественно, Go обогнал Erlang больше, чем на порядок. Потом я скомпилировал модуль Erlang с применением HiPE и расчет на Erlang оказался в 4 раза медленнее расчета на Go.
На этом я и остановился. Ощутимой прибавки производительности можно и добиться, но другие минусы расстроили. А целого ряда возможностей Erlang'а нет и скорее всего никогда не будет в Go. Поэтому Erlang и дальше для меня остается более предпочтительным в качестве платформы для разработки серверных приложений. Серьезные проблемы типа дедлоков в нем решаются вообще иной организацией построения приложений и иным подходом в решении задач. А Go это такой же язык, как и сотня других. Простой синтаксис и компиляция в двоичный код спасают скоростью своей работы. Но на этом пока и все. Реализация зеленых процессов и обмен сообщений между ними произвели впечатление каких-то сырых костылей, скрученных изолентой. Еще в процессе обсуждения в канале нашлась еще одна задача, которая тяжело решается на Go из за его принципиально статической природы: это прием и разбор json если он не имеет строго определенной структуры (мап, а в нем мап, а в мапе тоже мап, а может и нет). Так что можно предположить, что с парсингом xml, html там могут быть похожие проблемы. Так же есть информация, что http сервер на Go оптимальнее всего работает в один процесс. Тогда зачем все эти зеленые процессы и каналы?
Ну а в связи с тем, что статья про Go, я закончу на позитивной ноте. Go мне понравился тем, что я будучи совсем ничего не знающим про Go даже могу что-то понять из исходного кода. Концепт там такой, что кода больше, но зато всем понятно. Так вот — действительно общий смысл понятно. В том же Erlang многие вещи будут очень непонятны. На Go вполне удобно делать какие-то консольные утилиты (каких то wxWidgets пока не прикручено, чаще поднимают локальный вебсервер и заходят на него из браузера, но работы вроде бы идут). Полученный exe файл получается размером меньше 10 мегабайт и рядом не требуется укладывать дополнительных dll (тут мы вспоминаем приложение Qt). Есть и кросс компиляция на другие платформы. Сообщество активно, многолюдно. Каждый второй пишет какую-нибудь библиотеку для какой-нибудь задачи (csv, orm etc), которые находятся в разных состояниях готовности. Удачи в изучении и применении Go и по возможности дайте намётки на то, как бы вы поборолись с тем, за что я его ругаю ;)
Каждый второй пишет какую-нибудь библиотеку для какой-нибудь задачи (csv, orm etc), которые находятся в разных состояниях готовности.Как по мне, это жирный минус экосистемы Go. У меня есть опыт написания микросервисов на Go, но даже в рамках микросервиса этот буриданов выбор из кучи библиотек, которые делают одно и то же, начинает раздражать. Причём большинство из них напрочь лишены версионности и приходится линковать их к проекту по ревизии в master-ветке o_O
Кстати, для управления зависимостями в Go тоже 100500 альтернатив и ни одной, которая по удобству была бы близка к bundler или hex.
Для себя решил отказом от Go в пользу Elixir. Да, по памяти Go экономнее, но не критично. А по производительности для веб-сервера они одинаковы с точностью до погрешности. При этом код на Elixir получается проще, лаконичнее и понятнее. Хоть и требует, чтобы программист был в курсе функциональной парадигмы и потратил недельку на знакомство с OTP.
2. С помощью библиотеки runtime можно контролировать смену контекста. Ну в зацикливающиемся приложении вина не на языке.
Конкретно в веб проектах я участвую не очень давно. Но приходится как-то поддерживать две системы, одна на yii, вторая на codeigniter. Поэтому фреймворки я как-то не очень люблю. И не потому что PHP, а из за их замкнутости. Фреймворк загнулся — знания на помойку. Посадили за фреймворк «B», и знания по фреймворку «А» тебе не помогут. Если делать отдельно фронт и бекенд, связывая их стандартными протоколами, то части системы можно будет заменять. Сейчас вот работаю в проекте с AngularJS на фронте. Так он не вызывает внутреннего отторжения. И при разработке такой системы даже в случае с крахом ангуляра, знания и опыт по бекенду точно не пропадут. Либо наоборот, если фронт на AngularJS остается без изменений, а бекенд на скорую руку написан на PHP. После этого можно его переделать на Python или Go или на чем-то еще, в зависимости от того, каких разработчиков получится найти. Поэтому сейчас, когда захотелось написать фронт на ангуляре и бекенд на ерланге со связью через вебсокеты, мне пришлось поискать, кто из ерланг веб серверов умеет вебсокеты. В итоге я остановился на cowboy и yaws, последний выбрал по причине того, что он поставился на win ;) Но даже вот после этого я постарался выйти к тому, чтобы по максимуму отвязаться от типа выбранного сервера. Фронт устанавливает стандартное ws соединение. Сервер yaws это поддерживает в виде конечной точки и функции обратного вызова. В нее попадает поток из ws. В этом месте надо уже забыть про yaws и слать этот поток стандартному Erlang приложению. И тогда нормально будет. А то представьте, на 1/6 часть суши 1000 эрлангистов. Из них yaws знают 1/4. Даже с этой точки зрения найти людей будет проще. Но, возможно, я слишком глобально смотрю на этот вопрос. Если нужен стандартно-типовой проект, быстро что-то сделать, внедрить и забыть о нем, то фреймворки как раз позволяют это сделать. Так случилось и в моем случае: до меня запилили два решения(yii и codeigniter), а когда дошло дело до постоянных каких-то доработок (как это часто бывает на производстве)… а мне с этим жить )
Про плюсы Elixir недавно описал Юрий Жлоба из варгейминга. Я сделал для себя выводы, что знающих напрямую Erlang не особо интересует Elixir. И Exilir интересуются вроде бы как тех, кто в лоб Erlang тяжело воспринял. Однако, сложилось впечатление, что в Elixir более активное сообщество, библиотеки, системы установки модулей. Но не суть. Я думаю, по той причине, что elixir работает на VM Erlang, то под капотом там всё нормально (вспоминаем scala и JVM).
Извините, как-то опять много буков вышло.
Собственно, Plug — и есть центральная концепция, чем-то сродни middleware в Ruby, только используется единообразно на всю глубину стека: роутер — это plug, контролер — тоже plug и т.д.
За что ругают Golang и как с этим бороться?