Pull to refresh

Comments 127

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


Такие вещи надо, как минимум, обосновывать. А еще лучше доказывать.

Зачем использовать язык в котором «нет ничего нового»? Только вложить деньги в обучение команды и поиметь проблемы с заменяемостью сотрудников?
нет «инновационных» фишек. Просто взяли best practices и объединили в ортогональном подходе.
Какие именно best practices вы имеете ввиду?
Go in a Nutshell:
  • Imperative language
  • Statically typed
  • Syntax similar to Java/C/C++, but less parantheses and no semicolons
  • Compiles to native code (no JVM)
  • No classes, but structs with methods
  • Interfaces
  • No implementation inheritance. There's type embedding, though.
  • Functions are first class citizens
  • Functions can return multiple values
  • Go has closures
  • Pointers, but not pointer arithmetic
  • Built-in concurrency primitives: Goroutines and Channels

+ https://en.wikipedia.org/wiki/Go_(programming_language)#Language_design
Половина есть в любом современном языке, вторая половина пунктов спорные, как минимум.
Половина есть в любом современном языке
На то оно и best practices — проверено делом.
Обучение сотрудников — громко сказано, Go учится за неделю по докам на их же сайте (Effective Go), его стандарт (sic!) на 4 листах A4 умещается.
Кроме синтаксиса есть библиотеки, паттерны, интероп, деплоймент, вопросы быстродействия. Изучение всех этих вопросов займет еще месяца три минимум. Умножаем на количество разработчиков и зп в месяц, получаем космические цифры.

Тем более go еще сырой, то что уже решено в других языках в go потребует дополнительного траха.
Сырой? Да что вы говорите, какой «решено в других языках»?.. Написано за вас — да, сторонних пакетов у того же Ruby может быть больше, но вы же сами отнесли пакеты в категорию «учебного материала для сотрудников», иной раз написать необходимую функциональность будет быстрее, чем прикрутить и настроить (и отладить) сторонний пакет.

Насчёт паттернов, деплоймента и быстродействия — всё расписано в том же Effective Go. Да, звучит чудно, что освоить язык можно по одной страничке и сразу в бой, но это так. В прекрасное время живём.
> Кроме синтаксиса есть библиотеки, паттерны, интероп, деплоймент, вопросы быстродействия.

Это все входит в неделю. :) Синтаксис учится за день, деплоймент «статических» бинарников не требует особого изучения, большинство вопросов быстродействия снимаются после прочтения «Effective Go» по дороге на работу и обратно (ценители могут прочитать на обеде go memory model) и т.д.
Разумеется, это все касается тех, кто уже знаком, как минимум, с си, а не учащих go первым языком (или вторым после javascript-а).
ценители могут прочитать на обеде go memory model

И вообще, у них там замечательные записи в блоге об устройстве той или иной части языка, с интересом и удовольствием читаются.
Открыл спецификацию golang.org/ref/spec при печати на A4 получается 49 страниц. О каком стандарте на 4 страницы вы говорите?
Учебник по Go с такой же глубиной изложения будет не тоньше.

Кстати в спеку Go не включена стандартная библиотека, а спека C++ больше чем на половину из описания библиотеки состоит.

Ну и не забываем, что C++ уже 30 лет, а Go меньше пяти, да еще и ни в одном серьезном проекте не засветился.
да еще и ни в одном серьезном проекте не засветился.
Facebook, Youtube, сервисы Яндекса для начала. Список можно расширить ещё большим количеством примеров и вообще дополнить китайскими проектами. Т.е. написанное вами неверно вроде той степени, в какой неверно моё утверждение про 4 страницы A4.
И что конкретно в фейсбуке или яндексе на Go написано?
Facebook, к примеру, предложил эффективную реализацию джинериков для Go, которую в Google даже рассмотрят к внедрению в язык начиная с 2.0 github.com/facebookgo/generics
Я спрашиваю что в фейсбуке написано на Go.
То что в Go нужны женерики и без фейсбука было ясно очень давно.

Если что в FB огромный R&D штат, который может заниматься чем угодно, например делает свою версию стандартной библиотеки для C++.
Вы вправду не знаете крупные компании и проекты, которые написаны на Go или просто троллите?
Вот тут есть попытка собрать крупные компании, которые описывают свой опыт, но, как вы догадываетесь, большая часть компаний не рассказывает на публику про свой опыт в блог постах.
Но посмотрите, может быть знакомые компании увидите.

Currently using Go
  • Google — the core Go team work at Google. Most uses of Go at Google are confidential



Facebook, к примеру, предложил эффективную реализацию джинериков для Go

Note: This was a joke made for Gophercon2015. Sorry if you came here looking for something useful.


Хорошая шутка :)
Мммм… а вы по ссылке ходили?

Спойлер
image

Note: This was a joke made for Gophercon2015. Sorry if you came here looking for something useful.


Но если всерьез: то у них несколько страниц небольших модулей и утилиток для разных задач github.com/facebookgo
Это gandjustas не ходил. :)
Ну и по поводу толщины книжек:
image
Такие вещи надо, как минимум, обосновывать. А еще лучше доказывать.

Есть вещи, которые нельзя формально доказать — что некоторые хабражители успешно используют в качестве аргумента («не смог доказать — значит это не так»).
Если бы Вам и вправду было интересно обоснование пользы простоты языков, Вы бы точно не пропустили сотни статей и десятки докладов на эту тему.
А я их и не пропустил. Вот только не увидел как эта эфимерная «простота» выливается в измеримые характеристики, типа продуктивности, плотности багов и количества строк на единицу функционала.
Чтобы не запрещать использование «нового» и, главное, не следить за соблюдением запрета :)
> В Google стала общепринятой следующая процедура: если у кого-то возникала проблема с С++, то он отправлял email с просьбой о совете, и каждый прочитавший отвечал на него по-разному.

Звучит, мягко выражаясь, странно.
И, тем не менее, это так. Я думаю любой, кто работает с C++ достаточно долго сталкивался с тем, ситуаций, когда он пытался решить какую-нибудь проблему много часов (а то и дней), а потом узнавал о том, что, на самом деле, она решается «легко и элегантно» — но способом, про которые 90% C++ программистов никогда и не слышали. Я, например, узнал о том, как выглядит C++ аналог calloc'а (выделение памяти под массив с его одновременным обнулением) только после многих лет работы на C++. Попробуйте сами дать правильный ответ нет раскрывая spoiler.
Ответ спрятан тут
char *p = new char[size](); // Две круглые скобочки превращают malloc в calloc!
А теперь — попробуйте объяснить чем этот способ хуже, собственно, calloc'а.
Ответ спрятан тут
Версия с new явно обнуляет массив и тем самым делает его «грязным», calloc же ипользует mmap «под капотом», что для больших массивов заметно эффективнее.
Я достаточно давно работаю с С++, и никогда не имел проблем с С++. Точнее, мелкие проблемы возникают часто, и решаются либо за 5 минут гугления и чтения StackOverflow, либо за полчаса ожидания ответа на собственный вопрос на StackOverflow.
Имхо — С++ лучший, не побоюсь даже сказать, единственный современный компилируемый объектно-ориентированный язык для кросс-платформенной разработки.
Чем плодить языки, лучше бы мужики пошли в С++ standardization committee и улучишили язык по своему видению или, банально, дописали недостающие в std библиотеки.
Самая большая проблема C++ — это его невероятная сложность и ограниченная стандартная библиотека. Ну действительно — что это, чёрт побери, за кроссплатформенный язык в котором нет никакой возможности даже какую-нибудь внешнюю программу вызвать переносимым способом! Даже если boost подключить! Но это всё-таки меньшее зло: можно всё-таки довести до ума Boost.Process, добавить всяких библиотек для работы gzip'ы/bzip'ы… но всё это не нужно, так как на фоне изучения собственно C++ вопрос «а как подключить libbzip» кажется уже мелочью.

И вот со сложностью можно бороться только одним способом: созданием нового языка. К сожалению опора на GC обозначает, что заменить C/C++ Go, увы, не сможет. Сможет ли rust — не знаю, время покажет, но Go — точно не сможет.
Какая «невероятная сложность»? Да, есть нюансы, на изучение языка нужно потратить некоторое время. Но я бы не назвал С++ сложным. Он объёмный (много фич), но понятный.
>Он объёмный (много фич), но понятный.
Если вы считаете С++ простым и понятным, то значит вы не писали ни на чем другом кроме него.
Писал на Delphi, C#, Java. Немножко на Erlang :)
Он объёмный (много фич), но понятный.
«Много фич» — это и значит «непонятный».

У любого языка есть две сложности:
1. Сложность написания программ на этом языке.
2. Сложность чтения программ на этом языке.
И у них зависимость — едва ли не противоположная (чем богаче и выразительнее язык тем проще на нём писать, но и тем сложнее разбираться в хитромудростях написанного), и если не уточнять, то зачастую люди не работавшие над большими проектами (где количество кода меряется в миллионах строк) под термином «сложность языка» подразумевают именно первое. Потому что когда на проекте один человек (или несколько людей с один и тем же background'ом), то никого не напрягает сложность языка: вы просто используете его небольшое подмножество, которое вам нравится, а всё разнообразие его «чудес» вас не касается.

А вот когда над проектом начинает работать множество людей с разным образованием — вот тогда всё и начинается. Я это очень хорошо ощутил, когда мой коллега посмотрел на мой код, который я прислал ему на ревью, почти квадратными глазами. А там всего-то навсего использовался using в классе. И даже несмотря на то, что такое использование прямо разрешено в style guide код пришлось переписывать, чтобы другие разработчики от него не офигели. На больших проектах под сложность языка всегда подразумевается именно сложность чтения и понимания чужого кода.

И вот тут у C++ всё плохо. Контрольные вопросы: вы-то сами с кодом, подобным тому, который начал дискуссию, сталкивались? Смогли бы понять, что там происходит? А понять как вынос функции-друга описанной в теле класса (да-да, это возможно) из него наружу может сломать программу? Не просто привести к ошибке компиляции, а привести к тому, что всё упадёт при запуске? Вот этот вот простой пример в «простом» языке можете разобрать:
Маленький примерчик
$ cat test.cc
#include <iostream>

#ifndef IN_CLASS_DECLARATION
#define IN_CLASS_DECLARATION 1
#endif

#define FOO_FUNCTION \
  void foo(const cls<T> &a, T b) { \
   std::cout << "foo(T):" << a.t << " " << b << std::endl; \
  }

template<typename T>
class cls {
 public:
  cls(T t_) {
   t = t_;
  }
  operator T() {
   return t;
  }
#if IN_CLASS_DECLARATION
  friend FOO_FUNCTION
#endif
  T t;
};

#if !IN_CLASS_DECLARATION
template<typename T> FOO_FUNCTION
#endif

void foo(double, double) {
  std::cout << "oops" << std::endl;
}

int main() {
  cls<double> a = 1;
  foo(a, 1);
}
$ g++ -DIN_CLASS_DECLARATION=1 test.cc -o test-in
$ g++ -DIN_CLASS_DECLARATION=0 test.cc -o test-out
$ ./test-in
foo(T):1 1
$ ./test-out
oops
а в реальной прогрмме это была не одна функции foo, это был operator, который, понятно, взаимодействовал с кучей других operatorов из других включаемых файлов.

Это когда у вас в библиотеке много фич, вы можете сказать «она простая, но объёмная». Потому что любое использование этих фич видимо в коде. А вот использование фич языка в коде зачастую либо вообще невидимо, либо выглядит настолько невинно и незаметно, что не зная о них вы можете запросто порушить код и потом потратить неделю на его отладку. Продолжая пример выше: если у вас возле какого-нибудь невинного "T *p=new T();" не стоит комментарий, что скобочки тут не просто так, а по делу, то кто-нибудь может их и убрать — и компилятор ему слова дурного не скажет, а падать ваша программа начнёт самым странным и непредсказуемым способом.
Я не спорю, что на С++ можно написать никому не понятный код. Очень даже можно. Соглашусь, пожалуй, что в этом смысле язык сложный. Но сложный код писать не нужно. *Почти* любую задачу можно решить простым, красивым и понятным кодом. Например, new я уже 100 лет в обед как не использую.
Разговоры про «сложный код писать не нужно» вы можете вести, опять-таки, только в маленькой компании. А в большой — подразделения имеют разную историю, используют библиотеки купленные у разных поставщиков и т.д. и т.п.

Да и в примерах выше — речь-то идёт о довольно-таки банальных вещах. Вы можете сколько угодно хвалиться тем, что вы «100 лет в обед как не используете» new, но в один прекрасный момент вам это потребуется (ну, скажем, чтобы два процесса могли общаться через shared-memory) и вот тут-то у вас проблем возникнет — мало не покажется.
Другими словами, проблема не в языке, а в программистах. Ч. т. д. И нет, мне не понадобится new.
Нет, «проблема в программистах» может быть в случае, когда у вас нет выбора языка. Если вы работаете в команде, где вам ставят жесткие условия на чём писать, или вы студент, которому нужно делать лабораторную именно на этом языке — тогда да, ваше утверждение справедливо.

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

Поэтому дело именно в языке. «Добавочная сложность», которую привносит С++ в разработку для вас не кажется «сложностью», просто потому что вы к ней привыкли. В других языках, с которыми вы работали дела обстояли не намного лучше, поэтому это для вас некий baseline, это «норма». Но есть другие языки, которые вы не знаете (и, наверняка, не хотите по ряду вполне резонных причин), для которых эта «норма» давным давно уже неактуальна, в них уже своя норма, и, ценой компромиссов, «добавочная сложность» этих языков намного меньше.

И именно поэтому для многих людей, которые имеют возможность сравнивать, С++ уже однозначно является «переусложненным языком», причем сложность которого себя не окупает. А такие языки, как Go эти компромиссы ещё более уменьшают — настолько, что ниша С++ становится совсем уже узкой, да и ту Rust отбирает.

Извините, если где задел — я лишь пытаюсь подобрать правильные слова, чтобы помочь вам понять, почему столько людей (и я не только про Хабр, а про тех же авторов Go и Google) считают С++ «неоправданно сложным языком», а вы с этим не согласны.
Я думаю, что понимаю, о чём вы говорите, и согласен лишь отчасти.
Да, я действительно *пока что* не хочу изучать все эти Go, Rust, D и ещё полтора десятка непонятно кому нужных экзотических языков. Меня устраивает язык, которым я сейчас пользуюсь 95% времени, потому что считаю его лучшим из существующих на данный момент. И уж точно не считаю его сложным.

Более того, я надеюсь, что эти языки не взлетят и тихо загнутся. Потому что в этой сфере предпочтительна монополия. Проблема языков, как правило, не в самом языке, а в библиотеках для него. Чем больше библиотек, тем проще найти нужную и прикрутить в своё приложение вместо того, чтобы тратить время на имплементацию велосипеда, который уже писали тысячи человек до того… на других языках. Я хочу писать уникальную функциональность, а не очередную обёртку над threading API / библиотку для работы с UTF-8 строками / ещё какую-то фигню, которая сжирает кучу времени и без которой нельзя нормально реализовать высокоуровневую логику (можно ещё вспомнить ваш пример с вызовом внешнего процесса). А обилие языков только распыляет усилия программистов.
Так вас никто и не заставляет. Если вам комфортно с С++, ваши задачи, временные рамки, коллеги и условия позволяют спокойно его использовать — в чём проблема?

… не хочу изучать все эти Go, Rust, D и ещё полтора десятка… Меня устраивает язык, которым я сейчас пользуюсь 95% времени, потому что считаю его лучшим из существующих на данный момент.

Отсюда и конфликт в дискуссиях. Я, к примеру, писал долгое время на С++, и уже 2.5 года пишу на Go, поэтому могу объективно сравнить оба языка. Вы же, из этих двух языках, на практике знаете лишь один, но считаете его лучше. Это необъективно. Любая ваша аргументация поэтому обречена быть чисто теоретической, а споры бессмысленными.
Ну как можно доказывать, что инструмент А, лучше инструмента Б, если вы никогда не держали в руке Б?

Чем больше библиотек, тем проще найти нужную и прикрутить в своё приложение вместо того, чтобы тратить время на имплементацию велосипеда, который уже писали тысячи человек до того… на других языках. Я хочу писать уникальную функциональность, а не очередную обёртку над threading API / библиотку для работы с UTF-8 строками / ещё какую-то фигню, которая сжирает кучу времени и без которой нельзя нормально реализовать высокоуровневую логику

Кстати, насчет «найти и прикрутить библиотеку» — в С++ этот процесс занимает в лучшем случае десятки минут (и то, если уже раньше с этой библиотекой работал) — найти, скачать, собрать, установить, прописать нужные пути импортов и линкера, разобраться в документации (которая у каждой либы по своему сделана), добавить код. И это редко работает гладко, обычно это возня на целый вечер.
В Go простота этого процесса доведена до предела и занимает секунды: одна команда «go get url-проекта» и одна строчка «import url-проекта» в коде. При этом все существующие библиотеки ищутся централизованно через godoc.org и там же всегда доступна стандартная форма документации, которая есть у подавляющего большинства библиотек в Go.
И если в С++ задача «попробовать 5 разных библиотек» занимала несколько дней, то в Go занимает несколько минут.
И так во многих других аспектах.
Ирония в том, что вы вроде бы понимаете, что хотите «писать уникальную функциональность», а не тратить время на возню с библиотеками и обертками. Но это именно то, на что в С++ уходит чересчур много времени.
И, да, UTF8, фантастическая конкурентность (threading API это жалкое подобие) и 90% «еще какой-то фигни», наличием библиотек для которых вы хвалитесь в С++ — в Go идёт из коробки, и очень хорошо продуманы. По факту, то, что есть в стандартной библиотеке Go достаточно для 80% современного системных/серверных софта.

Вобщем, мой поинт в том, что только поработав, и сравнив на личном опыте оба языка, вы сможете объективно их оценивать. Не раньше.

Более того, я надеюсь, что эти языки не взлетят и тихо загнутся.

Ну тут без комментариев )
Я видел синтаксис инструмента В. А также С и D. Спасибо, не надо, я увидел достаточно. Уберите эти орудия пыток.

> В Go простота этого процесса доведена до предела и занимает секунды: одна команда «go get url-проекта» и одна строчка «import url-проекта» в коде.

Я думаю, это компромисс удобство(=жётская стандартизация) / гибкость (=бардак, каждый творит, что хочет). Go идёт по пути удобства, С++ — гибкости. Кажому своё. Лёгкость подключения библиотек — это классно, но их кто-то ещё должен сначала написать.

>Вобщем, мой поинт в том, что только поработав, и сравнив на личном опыте оба языка, вы сможете объективно их оценивать. Не раньше.

Да, это так, не буду спорить. Но выше я уже написал, что конкретно Go мне не нравится.
> В Go простота этого процесса доведена до предела и занимает секунды: одна команда «go get url-проекта» и одна строчка «import url-проекта» в коде.

Ага. А потом, внезапно, приходит понимание, что pin to master — не лучший способ управления зависимостями, и приходится «прикручивать библиотеки». То есть, фактически, go в этом плане ничего не решает — он просто откладывает решение проблемы.
«Много фич» — это и значит «непонятный».

Вот только «Мало фич» вовсе не означает «Понятный». Но про это что-то все забывают.

Есть языки с предельно малыми количествами языковых фич — brainfuck и lisp (scheme). Оба не отличаются понятностью. Хотя scheme при этом намного мощнее того же Go.
Go разрабатывался с целью, чтобы код выглядел максимально и читался легко. Это так и есть, судя по личным ощущениям и отзывам различных компаний, которые не боятся показывать куски кода менеджерам и даже те понимают, что нельзя сказать про Ruby, одно время который пытались преподавать аж детям.
Читать код — навык который можно и нужно тренировать. От языка этот навык не зависит. Запутать код можно вообще на любом языке. Почти на любом языке можно код превратить в подобие eDSL, который будет читаться даже неспециалистом.

Мне, например, каналы и слайсы в go рвут мозг. Мешанина значков, которые никак не могу запомнить как работают. Я бы предпочел библиотечные функции вместо этой хрени.
А еще бесит отсутствие комбинаторов\list comprehensions или вообще любых декларативных способов обработки списков. Любой более-менее сложный код превращается в тучу вложенных циклов.
Мешанина значков, которые никак не могу запомнить как работают.

Ровно два значка:
<- = стрелочка «данные идут справа налево»
[] = это квадратные скобочки

Какой именно из этих целых двух значков вам тут рвёт мозг?
Вот только смысл значка зависит от того где значок написан. Это и бесит. Кстати в других языках подобного идиотизма нет.
Ну да, аж целых два use-кейса:
item := <- myChannel

и
myChannel <- item


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

Ну и
:= <-

Это пять. Даже в хаскелях таких вещей не встретишь.
Мне тоже первый раз это показалось вырвиглазым.
Зато все понятно и нельзя понять двояко: объявить переменную нужного типа и присвоить ей то, что вычитаешь из канала.
Я достаточно давно работаю с С++, и никогда не имел проблем с С++

Но их имели программисты всего мира, включая программистов Google. Будь С++ таким, как вы его хотите видеть, не появился бы ни Go, ни Rust, все бы продолжали писать на С++, не сомневайтесь.
Отличный доклад, но «интерпретируемый эрланг»…
Ну, будем честны — erlang компилируется в байткод, который и вправду интерпретируется. Ни JIT, ни AOT нет.
Впрочем, все знают — erlang выбирают не за raw performance.

А что у Go с протоколами типа SOAP, как он работает с XML, есть ли библиотеки работы с SMTP, HTTP? В общем, есть ли свой 'CPAN'?
В стандартной библиотеке есть средства для работы, как с HTPP, так и с SMTP
> как он работает с XML

В стандартной библиотеке есть поддержка маршаллинга аннотированных структур в xml и обратно.

> SMTP, HTTP

Клиент SMTP и сервер/клиент HTTP есть, опять же, в стандартной библиотеке.

> В общем, есть ли свой 'CPAN'?

CPAN-ом для go фактически является github. В проект можно импортировать напрямую из git/svn/hg репозиотриев (есть батарейки для популярных хостингов). Из минусов, пожалуй, есть лишь отсутствие встроенной поддержки указания конкретных тегов/бранчей для git реп, т.е. всегда берется последняя ревизия из дефолтной ветки, но это частично решается внешними костылями, вроде, gopkg.in.
А SOAP? Можно ли построить на нём сервер и клиент, с полной функциональностью 1 и 2, есть ли несовместимости с другими серверами/клиентами?
SOAP нынче стоит в одном ряду со словами «dead», «masocоstic» и «zombie», поэтому в стандартной библиотеке, конечно же его нет.
Посмотрите вот, какие-то пакеты есть, но более детально не подскажу: godoc.org/?q=soap

Вот еще статья на тему: taoofmac.com/space/blog/2014/05/11/1121
Я немного возился с SOAP и клиентом при помощи стандартных библиотек в ноябре прошлого года, это было не самое приятное занятие (особенно если сравнивать по удобству с SoapClient в PHP), однако вроде работало. Что там сейчас — надо смотреть, возможно с библиотеками стало удобнее.
Вопрос в полной поддержке SOAP сервера и клиента — схема, WS сервисы и так далее, совместимость с другими SOAP серверами и клиентами. И не только клиента, но и сервера. То есть можно ли на Go писать реальные сервера, которые будут удовлетворять стандартам.
То, что лежит на гитхабе, выглядит непригодным.
То есть можно ли на Go писать реальные сервера, которые будут удовлетворять стандартам.

Можно. Но SOAP и «реальные сервера» это не синонимы, как может показаться из вашего ответа.

PS. readwrite.com/2011/05/26/soap-is-not-dead---its-undead
Я знаю один пример создания системы из десятков SOAP серверов, сделанных десятками программистских коллективов из разных стран, которые работают совместно и работают хорошо. Я не представляю, на какой другой технологии может быть реализовано подобное.
Все высеры типа «SOAP undead zombie» стоят столько же, сколько стоят любые высеры типа «99% веб работает на PHP, значит всё остальное зомби», то есть нисколько не стоят.
Я не представляю, на какой другой технологии может быть реализовано подобное.
ProtoBuf и его развитие Cap'n Proto отлично справятся. Правда в C++. Как оно там в Go — не знаю.
Ну протобаф в Go по понятным причинам поддерживается великолепно, Cap'n'Proto для Go есть на офсайте.
У меня всегда было ощущение, что SOAP хорошо живет только в proprietary-системах, причем в экосистеме Microsoft, и это была одна из причин, почему REST победил. Я заблуждаюсь?
Конечно заблуждаетесь. SOAP и REST настолько разные, что их нельзя сравнивать — нет пересекающихся категорий. И уж тем более нет никаких оснований для «только в proprietary-системах, причем в экосистеме Microsoft». REST победил там, где не осилили все прелести SOAP и где непринципиально получить или не получить ответ.

Но это не важно. Go — это язык прикладной. В нём должны быть средства для написания программ.
Согласен. но Go всё таки в равной степени детище как Google, так и open-source сообщества, и если SOAP так востребован, важен и популярен, то нелогично, что нет достойных реализаций. Под любые другие протоколы, технологии и экосистемы, востребованные и популярные в это время — всё есть, если не в стандартной библиотеке, то third party. Тут же спрос решает в конце-концов.
Так что, либо я не так уж заблуждаюсь насчёт SOAP, либо есть иное объяснение. Но интересно, спасибо за мнение, постараюсь углубиться в тему чуть глубже.
Я посмотрел ещё в сторону анализа почтовых форматов хранения — та же история. net.mail в стандартной библиотеке очень простая библиотека, почтовые форматы поддерживает на уровне выбрать Subjеct. Смотрю godoc.org/github.com/luksen/maildir — и всё время вопрос: а где примеры? Как переместить письмо в другой фолдер (в контексте Maildir), что понимает автор вообще под Maildir? После перлового Mail::Box perl.overmeer.net/mailbox/html/index.html такие библиотеки и такая документация вызывают странное ощущение — то ли я идиот, то-ли автор библиотеки делал что-то лично для себя.

То есть куда я ни глянул под свои задачи — Go не имеет внятного решения. И не имеет не только решения, но и внятного объяснения откуда брать библиотеки, как они сочетаются друг с другом, как вообще сформировать набор совместимых библиотек. За что ругают Руби — надо под каждый проект собирать файлы нужных вверсий. Так в нём это хотя бы возможно. А какие механизмы для Go? Лазить в чужие репозитории Git? А в них есть эти зависимости?

Какие вообще задачи Go способен решить? Не имея библиотек?
Типичные детские болезни. Лет через 5 будет всё как у взрослых :-)
Конечно, количество библиотек будет расти.

Но я считаю неадекватной оценку «Не имея библиотек» из-за того, что человеку нужно работать с устаревшими и непопулярными вещами. Здесь всё решает спрос, в конце концов, и для всего, что сейчас в современной экосистеме сетевого/облачного софта востребовано, в Go великолепная поддержка.

Ну и я уже не говорю, что Go — это язык программирования, в конце концов, и библиотеки пишутся такими же людьми, у которых возникла надобность в том или ином решении. Если msgpack востребован, то под него и куча решений, а разбор Maildir — вероятно не такая частая задача в наши дни, и, если вам не подошла библиотека («где примеры?») — всегда можно написать свою, с примерами.
Это SOAP непопулярен и устарел? Только потому, что SOAP занимает 17% от RPC интерфейсов? Тогда GO устарел, так как он занимает ничтожный процент от любого языка из десятки-сотни ведущих языков?

Это форматы почтовых файлов и электронная почта устарела? Разбор Maildir это нечастая задача?
Что, в экосистеме облачного софта электронной почты больше нет?

Отсутствие в Go библиотек может быть по нескольким причинам. Например, 1) не успели написать, 2) не пишут на Go сложные ответственные программы так как непопулярен он пока. Но мой последний параграф

«То есть куда я ни глянул под свои задачи — Go не имеет внятного решения. И не имеет не только решения, но и внятного объяснения откуда брать библиотеки, как они сочетаются друг с другом, как вообще сформировать набор совместимых библиотек. За что ругают Руби — надо под каждый проект собирать файлы нужных вверсий. Так в нём это хотя бы возможно. А какие механизмы для Go? Лазить в чужие репозитории Git? А в них есть эти зависимости?»

как можно оправдать облачными сервисами? Как оправдать отсутствие документации даже для стандартных библиотек?
Смотрите, есть ситуация — Google, Dropbox, Docker, StackOverflow и куча других компаний, плюс еще тысячи людей успешно пишут проекты на Go, и говорят, что Go давным давно прошел стадию «нехватки библиотек».
У вас есть юз-кейс, где для одной спорной технологии вам не хватает одной библиотеки. Вы приплетаете сюда ещё вторую библиотеку для тоже весьма спорной по популярности задачи, в которой вам не нравится отсутствие примеров, и называете это «в Go нет библиотек»?

Давай быть объективными. Конечно, в Perl-е больше библиотек для устаревших или устаревающих технологий и задач. Но в Go их больше для новых и современных, для работы в облаке, для работы с современными форматами и протоколами, клиентов для новых серверных технологий и решений.

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

PS. Я не хочу спорить про SOAP. Но я уверен, что Google Trends не врут (и это совпадает с моим пониманием востребованности), и что, будь SOAP так популярен — выбор библиотек был у вас такой же, как для других современных протоколов/технологий.
Увы, пакета net/soap нет в стандартной библиотеке, и по всей видимости вряд ли он когда-нибудь появится. Встроенного клиента/сервера нет. Поэтому о полной поддержке речи не идет точно. Остаются проекты пользователей неизвестного качества.

В стандартной библиотеке нет поддержки WSDL, поэтому приходится вручную описывать структуры. Полузаброшенные костыли на Github вроде этого и этого (посвежее) умеют генерить из XSD/WSDL .go-файлы со структурами. Для валидации по схеме ничего нет — этот вопрос время от времени возникал в рассылке go-nuts и оставался без ответов (предлагали взять биндинги к libxml2 и написать самостоятельно). Про совместимость ничего не скажу, предполагается, что придется генерить запросы руками и разбирать их также.

В основном гуглятся только простые примеры вроде такого. Полезный опыт описан здесь и здесь, по второй ссылке приведен более сложный и интересный пример вместе с жалобами на трудности работы с encoding/xml.

Я эксперименты с SOAP в Go прекратил, поэтому точнее не подскажу. Сам интереса ради хочу попробовать чистый C + gSOAP/cSOAP.
Там были и более интересные и практичные доклады :) Так, например, доклад от нашего соотечественника — Димы Вьюкова про различные утилиты для Go.
Год назад я заинтересовался Go как раз после замечательного доклада Дмитрия на DevConf — помнится, в том числе чатик он там писал за пять минут. В результате я увлекся историей развития ЯП (и вещами вроде Plan 9), их более современными фичами и алгоритмами, поэтому мне близко было именно это выступление. По-моему, до трех ночи я не засиживался уже месяца три, тем более в пятницу вечером и за переводом.

Честно говоря, было бы лучше, если бы Дмитрий сам опубликовал пост по теме на Хабре — для меня как-то странно было бы переводить с английского на русский то, что изначально было на русском :) В любом случае, насколько мне известно, перевод его выступления должен скоро появиться на Хабре.
Про историю go ещё вот этот вот доклад был: www.gophercon.com/talks/roots-of-go
Правда, на мой взгляд, он был очень скучный и унылый. Тот, который в этом посте — интереснее.

А Дима вообще красавчик, да :)
Меня одного напрягает обилие восхваляющих Go статей, без единой строчки кода?

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

Или вот про дженерики пишут «здесь есть определенные компромиссы, с которыми придется считаться: например, придется выбирать, хотите ли вы иметь бинарник большего размера, или же более медленный бинарник, или же писать больше исходного кода.». Отказ от дженериков — это как раз третий вариант «писать больше исходного кода», после чего необходимыми становятся всякие gofmt/gofix для обуздания копипасты.

Меня одного напрягает обилие восхваляющих Go статей, без единой строчки кода?

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

Как в Go решена проблема структурной типизации вида: есть два различных интерфейса с похожими с точностью до сигнатуры методами, как реализовать оба интерфейса, если реализации их должны быть различны?

В Go нет «проблемы структурной типизации». Интерфейс — это описание поведения, а не реализации. Если два интерфейса содержат идентичные методы, то это два одинаковых интерфейса.
Но, возможно, я не совсем верно понял ваш вопрос, тогда приведите практический пример, постараюсь ответить точнее.
Подобные статьи общего плана можно написать и про brainfuck. Без конкретных примеров, где были бы видны преимущества, такие маркетинговые статьи «для привлечения внимания» ничего не стоят.

Наглядный пример (не на Go):

interface XMLSerializable {
    serialize() : String
}

interface JSONSerializable {
    serialize() : String
}

class Human {
    serialize() {
        return "human name=Jim"
    }
}

function createJSONResponse( obj : JSONSerializable ) {
    return new JSONResponse( obj.serialize() )
}

createJSONResponse( new Human )


Подобные статьи общего плана можно написать и про brainfuck. Без конкретных примеров, где были бы видны преимущества, такие маркетинговые статьи «для привлечения внимания» ничего не стоят.

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

Ваш пример, наверное, не самый удачный, потому что эта задача (сериализации) в Go красиво решается с помощью тегов структур и стандартной библиотеки.
В пакетах encoding/json и encoding/xml есть интерфейсы Marshaller — json.Marshaller и xml.Marshaller соответственно. Но у них разные сигнатуры, MarshalJSON и MarshalXML. Любой тип, который хочет переопределить сериализацию в JSON или в XML — просто добавляет методы MarshalJSON и MarshalXML соответственно.
Вместо:
type Human struct {
    Name string `json:"name"`
}

h := Human{Name:"Jim"}
data, err := json.Marshal(h)

play.golang.org/p/RzO2LWI8dV

делается что-то вроде
// MarshalJSON implements Marshaller for Human
func (h Human) MarshalJSON() ([]byte, error) {
	return []byte(`{"human name": "` + h.Name + `"}`), nil
}

play.golang.org/p/tKxJ2OlEYj

Но, опять же, в реальной практике в этом обычно нет нужды. К примеру, данный сниппет легко решается изменением тэга json (или xml) для соответствующего поля структуры:
type Human struct {
	Name string `json:"human name", xml:"HumanName"`
}

play.golang.org/p/L-zHKlQEJs
Мой пример наглядно демонстрирует проблему структурной типизации. Постарайтесь абстрагироваться от конкретных интерфейсов, каждый из которых может быть описан в отдельном модуле и вообще не знать о существовании друг друга. Наличие одинаковых сигнатур в разных интерфейсах не говорит о том, что функции эти делают одно и то же.
Постарайтесь абстрагироваться от конкретных интерфейсов, каждый из которых может быть описан в отдельном модуле и вообще не знать о существовании друг друга.

Боюсь, что вы сейчас пытаетесь загнать себя (и меня) в логическую ловушку. Этой проблемы нет в Go by design, и вы просите решения несуществующей проблемы.

В спеке Go четко написано:
Two interface types are identical if they have the same set of methods with the same names and identical function types. Lower-case method names from different packages are always different. The order of the methods is irrelevant.


В Go, интерфейс это буквально «описание поведения», и если два интерфейса содержат одинаковые методы, то это одинаковые интерфейсы.

Взяв кальку с вашего примера:
type XMLSerializer interface {
    serialize()
}
type JSONSerializer interface {
    serialize()
}

Это два одинаковых интерфейса.
Любой тип, который реализует serialize() будет удовлетворять оба интерфейса, но это бессмысленно в Go, потому что интерфейс описывает поведение («сериализовать»), а не конкретную реализацию. Нужен JSON — создаете type Json struct{} и определяете serialize() для него. Нужен XML — создаете новый тип type XML struct{} и пишете свой serialize(). Создавать два интерфейса в этом примере бессмысленно. Тогда уже больше подойдет единый type Serializer interface.
Всё ясно. Если закрывать на проблему глаза, то её как бы и не существует.

Это два одинаковых интерфейса.

В том-то и беда. Логически это два разных интерфейса. Структурно же они идентичны, что вводит компилятор в заблуждение об их эквивалентности. И с ростом приложений эти любезно расставленные грабли начнут стрелять. Приятной вам отладки :-)
Структурно же они идентичны, что вводит компилятор в заблуждение об их эквивалентности.

Не вводит, потому что так код не пишется в Go. Вы переносите паттерны с других языков с другой системой типов, и теоретизируете о несуществующей проблеме, серьезно.
Ну так баги никто специально не пишет. Они сами появляются когда где-то что-то кто-то не доглядит.
Но это совершенно не имеет никакого отношения к описанной вами проблеме. Устроить коллизию имён можно в любом языке.
В том же D я затрудняюсь сказать как устроить коллизию так, чтобы компилятор не ругнулся на это и не потребовал явным образом эту коллизию разрешить.
Вы никак не можете понять, что проблема исскусственная. У вас нет тут двух интерфейсов с одинаковыми сигнатурами. У вас тут два разных интерфейса: в одном есть функция XMLSerializer::serialize(), в другом — совсем другая (хотя, может быть, и похожая) функция JSONSerializer::serialize(). У них одинаковые входные/выходные аргументы и даже часть названия, но это разные функции. Если вы будете в своей документации ссылаться на обе как на serialize(), то никто ничего не поймёт.

Просто в некоторых языках принято выносить часть названия функции в название интерфейса, что позволяет кое-где ссылаться на эти функции не по полному имени, а по сокращённому, вот и всё. Разработчики Go решили, что это «упрощение» таковым не является и от этой практики отказались. Всё. Где вы видите проблему?
Этот serialize похоже никак не даёт вам покоя :-) Ок, попробуем так:

// Интерфейс человеков
type Human interface {
	Run()
}

// Интерфейс просессов операционной системы
type Process interface {
	Run()
}

// Класс весёлых персонажей
type Person struct {
	Name    string
}
func (c *Person) Run() {
	fmt.Println("Hello, my name is ", c.Name , " and i am running")
}

// Класс разрушительных процессов
type WorldDestroyer struct {
	Name    string
}
func (c *WorldDestroyer) Run() {
	Destroy( &World{Name: c.Name} )
}

// функция для игры с весёлыми персонажами
func playInFunnyGames(h1 Human, h2 Human) {
	h1.Run()
	h2.Run()
        fmt.Println("Innocent game complete")
}

// Создали первого персонажа
p1 := &Person{Name: "Jim"}

// Ой, где-то ошиблись и в переменной оказался объект совсем не того класса
p2 := &WorldDestroyer{Name: "Lucky"}

// Молодёжная комедия превращается в фильм-катастрофу
playInFunnyGames( p1 , p2 )
Это вам не даёт покоя высосанная из пальца проблема.

// Интерфейс человеков
type Human interface {
	Run()
}
Здесь вы написали буквально следующее: человек — это такая хрень, которая умеет бегать. То есть лошадь — это у нас человек, процесс — это тоже человек, и вообще — куча вещей, которые явно людьми не являются — всё человеки. Вам не кажется это… хмм… несколько странным?

Ну да ладно. Пусть у нас человек — это тот, кто бегает. И лошадь тоже. И процесс. Ну вот такой у нас ограниченный мир. Ладно. В чём же проблема?


  // Ой, где-то ошиблись и в переменной оказался объект совсем не того класса
  p2 := &WorldDestroyer{Name: "Lucky"}
Оказывается проблема у нас не в том, что у нас интерфейс Human неотличим от интерфейса Process, а в том, что ошибки определённого класса у нас не отлавливаются. Ну да. А кто сказал, что язык обязан такие ошибки отлавливать? На практике эта проблема «случайно» не возникает.

Вернее возникает — но не совсем эта. На практике я гораздо чаще наблюдаю ситуации когда объекты реализовывающие одинаковые интерфейсы путаются (засунули «задачу» не в тот «пул задач», положили «персонажей» не в ту «главу»), чем когда вы «случайно» засовываете куда-то не туда объект из совсем другой оперы. Так стоит ли переживать из-за того, что в Go у вас механизм, который решал 1% (ну 5%, от силы — 10%) проблемы перестал действовать? Зато он же и перестал требовать костылей в случаях, которые у вас в «нормальных» языках разруливаются с большим трудом.
Ну да ладно. Пусть у нас человек — это тот, кто бегает. И лошадь тоже. И процесс. Ну вот такой у нас ограниченный мир.
Интересно, как вы без помощи номинативной типизации сможете отличить человека от, например, обезьяны или андроида. Дайте догадаюсь, введёте уникальный для каждого вида метод типа такого:

type Human interface {
    iAmHuman()
    Run()
}
type Monkey interface {
    iAmMonkey()
    Run()
}

И получите эмуляцию номинативной типизации.

Оказывается проблема у нас не в том, что у нас интерфейс Human неотличим от интерфейса Process, а в том, что ошибки определённого класса у нас не отлавливаются. Ну да.
И не отлавливаются они потому, что эти два интерфейса не отличимы. Наконец-то вы признали эту проблему :-) Теперь о следующей:
1. мы подключаем две библиотеки из ортогональных областей
2. мы создаём свой кдласс, объекты которого хотелось бы использовать и с одной и с другой библиотекой
3. первая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать строку удовлетворяющую некоторым ограничениям: он должен состоять исключительно из цифробуквенных символов
4. вторая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать валидный идентификатор в БД вида "#\d+:\d+"
5. первый идентификатор можно получить из второго не сложными преобразованиями над строками (base32 например)
6. как в одном объекте реализовать оба варианта getId, чтобы один и тот же объект можно было использовать с обеими библиотеками?

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

Так стоит ли переживать из-за того, что в Go у вас механизм, который решал 1% (ну 5%, от силы — 10%) проблемы перестал действовать?
Перила пригождаются гораздо реже 1% случаев, но когда пригождаются ты понимаешь, что стоят они тут не зря.

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

class StorableShape : Shape { // наследуемся от фигуры
     private DBObject _store; // агрегируем совсем другой объект
     alias _store this; // становимся подтипом этого другого объекта
     this() {
          _store = new DBObject;
     }
     ...
}

Целая одна дополнительная строчка кода :-)
Интересно, как вы без помощи номинативной типизации сможете отличить человека от, например, обезьяны или андроида.
А зачем вам отличать обезьяну от андроида? Поймите вы: если у вас возникла проблема «как отличить обезьяну от человека», значит вы что-то сделали не так. Если в вашем мире нет никаких действий, которые может исполнять андроид, но не может исполнять обезьяна, то вам их и отличать не нужно, вот и всё. Если есть — то они автоматически станут отличимы и будут отличаться там и тогда, где это нужно.

Теперь о следующей:
1. мы подключаем две библиотеки из ортогональных областей
2. мы создаём свой кдласс, объекты которого хотелось бы использовать и с одной и с другой библиотекой
3. первая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать строку удовлетворяющую некоторым ограничениям: он должен состоять исключительно из цифробуквенных символов
4. вторая библиотека в интерфейсе указала, что этот объект должен реализовывать метод getId(), который должен возвращать валидный идентификатор в БД вида "#\d+:\d+"
5. первый идентификатор можно получить из второго не сложными преобразованиями над строками (base32 например)
6. как в одном объекте реализовать оба варианта getId, чтобы один и тот же объект можно было использовать с обеими библиотеками?
О ужас! О качмар! О боги, это ж никак нельзя разрулить. А, кстати, как вы разрулите эту проблему в Java? Если у вас первая библиотека будет требовать, чтобы класс реализовывал интерфейс AlphaNumerable, а вторая DbNumerable — оба с методами GetId? И кто помешает это же решение применить в Go?

И в любом случае если интерфейс накладывает ограничения на то, какой Id он хочет получить, но не указывает этого в названии метода, то это — плохой интерфейс, как вам уже divan0 отписал.
Затем, что с человеком человеку общаться будет интересно, а с андроидом — нет. У обоих есть метод «поддержать беседу». Вы же предлагаете ввести метод «поддержать беседу не как с роботом». Причём для этого вам придётся изменять стороннюю библиотеку, чтобы она вызывала именно его, а не просто «поддержать беседу». Вы глубоко ошибаетесь полагая, что «правильное именование методов» каким-либо образом спасёт вас от данной проблемы. Разве что сделает её появление менее вероятным.

В Java эта проблема не разруливается, поэтому там принято давать многословные имена, во избежание возможных конфликтов :-) Вот в C#, например, в случае неоднозначности компилятор настаивает на явном указании к какому интерфейсу относится реализация. Рекомендую почитать эту статью: www.javaportal.ru/java/articles/mnj.html
Вы глубоко ошибаетесь полагая, что «правильное именование методов» каким-либо образом спасёт вас от данной проблемы.
А почему нет?

В Java эта проблема не разруливается
А тогда что мы вообще тут сейчас обсуждаем? Если эта проблема не мешает создавать «ынтырпрайзных» монстров на миллионы строк на Java, то почему она помешает их создавать на Go? А если не создавать программы при которых ограничение на 2GiB кода в одном бинарнике становится проблемой, то и вообще проблем не будет.

поэтому там принято давать многословные имена, во избежание возможных конфликтов :-)
То есть всё-таки проблема разруливается?

Вы уж определитесь: у вас есть реальная проблема, которая нам страшно мешает жить (тогда Java будет, по вашему же признанию, контрпримером), либо у вас есть высосанная из пальца проблема, которая, может быть, себя проявит (а может и не проявит), когда вы будете создавать системы на триллионы строк кода миллионами программистов. В этом случае готов согласится — возможно «в далёкой галактике» с триллионами жителей и миллиардами программистов Go и не приживётся. Но мы-то пока на Земле живём!
Потому что невозможно синхронизовать работу тысяч программистов, чтобы они не использовали одни и те же имена для разных сущностей в своих библиотеках.

Миллионы строк можно на чём угодно написать. Вон, на том же С++ написано куда больше строк. Значит ли это что все проблемы, на которые так любят пинать любители Go, настолько несущественны, что ими стоит пренебречь, Go нужно закопать и всем взяться за изучение свежего сборника граблей С++15?

А бы предпочёл пользоваться инструментом который
… который гарантирует отсутствие неожиданностей, а не оставляет им место в тёмном углу, куда мало кто заглядывает.
Не будет вам никаких неожиданностей. Если у вас возникнет коллизия имён, то ваша программа просто не скомпилируется. А все остальные проблемы, как мы уже обсуждали — это просто неумение работать с Go.
Значит ли это что все проблемы, на которые так любят пинать любители Go, настолько несущественны, что ими стоит пренебречь, Go нужно закопать и всем взяться за изучение свежего сборника граблей С++15?
Во-первых C++15 в природе нет и не будет. Вы уж определитесь — вы про C++14 или C++17 говорите. А во-вторых самая большая проблема C++ (как уже обсуждалось), которую призван решить Go — это его сложность. О чём, в частности, написано в обсуждаемой нами статье (или вы уже забыли). Единственная способ борьбы с ней — это умение говорить «нет». Соответственно любые проблемы в Go обсуждаются на в разрезе «если долго-долго высасывать проблему из пальца, то можно насосать вот это», а «мы X раз сталкивались с проблемой Y, решали её так-то и так-то, а теперь предлагаем всё-таки немного усложить язык, чтобы с ней больше не пришлось сталкиваться».

Пока у вас не будет реальной статистики никто вышей «проблемой», случающейся когда солнечное затмение приходится на пятницу, 13е, заниматься не будет, уж извините. Потому что всё обсуждение в итоге свелось вот к этому:
Потому что невозможно синхронизовать работу тысяч программистов, чтобы они не использовали одни и те же имена для разных сущностей в своих библиотеках.
Почему нельзя? У других как-то получаются. Да, бывают коллизии, ну так нужно связаться с разработчиками, обсудить их, разрулить, в крайнем случае. А в большинстве случаев, если у людей мозги есть, проблем не будет. Как-то же люди на C пишут, а там вообще плоское пространство глобальных имён. Так можно дойти до того, чтобы вообще запретить людям имена придумывать, а заставлять их использовать рандомно сгененированные последовательности букв и цифр. А то как бы чего не вышло. Единственная реальна проблема, которую несёт в себе подход Go — это возможные коллизии имён. Тут вы правильно заметили. Да, это [потенциальная] проблема, но на практике коллизии случаются достаточно редко для того, чтобы вокруг этой проблемы не нужно было выстраивать огород — вот и всё.
И всё же, читая «вы создаете класс», понятно, что вы пытаетесь создать уже знакомые вам ситуации там, где они не создаются просто потому, что задачи решаются иначе. В Go очень простая и понятная модель — структуры (классы, по вашему) — описывают свойства, а интерфейсы — описывают поведение. Это очень мощная модель, если ей не сопротивляться.

Ваши примеры вроде методов интерфейса iAmHuman() — нонсенс в Go, потому что интерфейс не определяет свойство, человек ты или обезъяна. Примите же это, наконец :)
Вы сейчас с JS программистом разговариваете. О каких «знакомых мне классах» вы говорите? ;-) Реализация классов через структуры+UFCS мне импонирует куда больше чем через конструкторы+прототипы. Там не менее не стоит закрывать на проблемы глаза и притворяться будто их нет. Развитие языков происходит только тогда, когда проблемы существующих осознаются и начинает искаться их решение. Правда зачастую одни проблемы меняются на другие.

Интерфейсы в Go просто группируют сигнатуры методов, чтобы уменьшить объём копипасты при объявлении функций. А вот поведение описывается функциями. При этом вы вынуждены публичным методам объектов давать глобально уникальные имена. Причём глобальные не в рамках класса, не в рамках пакета, не в рамках приложения, а в рамках всей экосистемы Go.

Это не мой пример, а вариант костыля. Другой вариант (ни чем принципиально не отличающийся впрочем) вы же приводите ниже.
Давайте вы сначала пройдете Tour of Go, а потом будете пытаться «поломать язык»? :)

Вообще, специально для людей вот с таким подходом, в разных докладах по Go говорилась фраза «Don't fight the language». Решайте практические задачи способами языка, а не приносите старые паттерны и жалуйтесь, что они не так работают, как вы ожидаете.
Поверьте, Go писали далеко не дураки, и вот так, сгоряча, даже не почитав основ, пытаться «раскусить» и найти там глупость у вас не выйдет.
Ну, тоесть, себя вы может и сможете в этом убедить, но это же не интересно :)
Ну ок, поверю вам на слово, что Go — идеальный язык. Все остальные ведь дураки, не прошедшие Tour of Go, раз заменили коды возвратов исключениями, одиночный диспатчинг множественным, а динамическую типизацию статической :-)
Ну ок, поверю вам на слово, что Go — идеальный язык.

Или покажите, где я такое сказал или забирайте свое обвинение обратно )
Вы сейчас с JS программистом разговариваете. О каких «знакомых мне классах» вы говорите? ;-)
О всё тех же самых. Ваши мозги также безнадёжно искалечены Smalltalk'ом, как и мозги Java-разработчиков и C++-разработчиков.

При этом вы вынуждены публичным методам объектов давать глобально уникальные имена.
И вот тут это прекрасно видно. То, что у разработчиков Smalltalk'а случилось помутнение сознания и они засунули методы в объекты не означает, что так и должно быть. Подавляющее большинство методов объектам не принадлежат! Метод «изобрази себя» логически не принадлежит на объекту «круг», ни объекту «квадрат», ни объекту «экран», ни объекту «принтер» — он связывает между собой эти сущности. И именно за счёт этого, как правило, между разными методами не возникает коллизий даже с короткими именами.

В тех же редких случаях, когда коллизии возможны (как в примере с MarshalJSON/MarshalXML) у вас возникает связь не с двумя входящими объектами, а с входящим объектов и выходящим, а так как сигнатура выходящего в типе не участвует то возникает естественное желание внести её в имя метода). Поймите же вы, наконец, что эти методы названы так, как они названы не для того, чтобы «развести» потенциальные коллиззии, а чтобы облегчить чтение программы. Если у вас есть интерфейс, который включает в себя метод getId, который должен генерировать алфивитно-цифровой Id, то его название плохо не из-за того, что могут коллизии возникнуть, а из-за того, что такое название подразумевает, что вам годится любая строка в качестве Id, что есть неправда.

Если вы хотите писать на Go — то нужно писать на Go, а не пытаться придумать как выразить программу на C++, Java, или JavaScript на Go. Программу на C++ лучше всего писать на C++, а программу на JavaScript — таки на JavaScript, как это ни удивительно.
Хамство не сделает ваши аргументы более весомыми :-)

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

Если у вас есть интерфейс, который включает в себя метод getId, который должен генерировать алфивитно-цифровой Id, то его название плохо не из-за того, что могут коллизии возникнуть, а из-за того, что такое название подразумевает, что вам годится любая строка в качестве Id, что есть неправда.
Как же его правильно назвать? GetIdWithAlphaNumericalLettersAndUnderscoreOnly? :-)
видимо, .getUUID. Если уж мы приперлись решать проблему — давайте начнем с низов. Такие гадости, как GetIdWithAlphaNumericalLettersAndUnderscoreOnly — это мутанты, возникающие в силу того, что кто-то в свое время решил «а давайте-ка мы забабахаем свой стандарт идентификаторов!». И в результате n+1 стандарт идентификаторов. В проекте.
В одной библиотеке используется один стандарт, в другой — другой стандарт. Друг о друге они вообще не знают. Да и не должны. А нам надо работать и с ужом и с ежом — типична ситуация. UUID — это вообще другая опера, которая далеко не всегда годится.
На самом деле интересный пример, спасибо. В реальном мире, конечно, подобный код очень вряд ли встретиться, ещё и с «ой, где-то ошиблись», отсутствием доступа к оригинальным исходникам, описывающим интерфейсы и невозможностью адекватно переименовать.

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

Поэтому давайте объективно — если уж и произошел такой костыль, который нельзя исправить, то нужно искать workaround, если всё же хочется обезопасить себя на этапе компиляции. И workaround тут я вижу следующий — вместо Human использовать другой интерфейс-wrapper, отличный от Human. Вот как-то так (embedding наше все):
type MyHuman interface {
	Human
	MyRun()
}

func (c *Person) MyRun() {
	c.Run()
}

// ну и соответственно в playInFunnyGames использовать не Human, а MyHuman
func playInFunnyGames(h1 MyHuman, h2 MyHuman) {
	h1.Run()
	h2.Run()
	fmt.Println("Innocent game complete")
}

play.golang.org/p/EZrgR74HGd
Обойти эту проблему можно. Выкрутится по месту разными способами. Вопрос: стоит ли ради её решения усложнять язык? И тот факт, что ни Java, ни C++ этого не стали делать однозначно говорит нам о том, что проблема эта — скорее теоретическая и усложненять такой такой простой язык как Go ради неё явно не стоит.
Ну про «ломать язык» точно речь не идет. Но да, как же сложно class-based ООП-mindset выпрямлять )
Я продолжу этот пример:

// для человеков просто создаём песочницу и пусть бегают в ней
func RunInSandbox(h Human) {
    var sb = &SandBox{}
    sb.Put( h )
    h.Run()
}

// для процессов временно создаём изолированный контекст, который не влияет на текущий
func RunInSandbox(h Process) {
    IsolateContext()
    defer DestroyContext()
    h.Run()
}

// создаём уничтожитель миров
w := &WorldDestroyer{Name: "Lucky"}

// какая реализация будет вызвана?
runInSandbox( w )

какая реализация будет вызвана?

Будет вызывана реализация «RunInSandbox redeclared».
В Go нет оверлоада функций.

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

Ошибку не получите, потому что в случае двух разных пакаджей это будут две разных функции: packageA.RunInSandbox() и packageB.RunInSandbox().
Вы пока не убедили, что его стоит изучать ;-)

Ок, а что будет с вызовом w.RunInSandbox() или такой вызов разрешён только для функций параметризированных конкретной структурой?
Именно. Познакомьтесь с системой типов в Go, станет понятно, и всё это гадание на кофейной гуще станет не нужным.
В Go, как уже выше писалось, типы различаются на «интерфейсный тип» и «конкретный тип», и любой конкретный тип может неявно удовлетворять «интерфейсный тип». Там используется смешанный подход early/late binding, concrete types чекаются компилятором, интерфейсные типы чекаются рантаймом.

А ресивер метода да, может быть определен только на «конкретный тип». В спеке это четко описано.
Очень жаль, полноценный UFCS — полезная штука.
Вам жаль, что фишки из совершенно другого языка, надобность которого в Go реализуется другим путем, нету в Go?

image
Мне жаль, что удобные паттерны не реализуются по причине «у нас тут всё по своему, мы же не дураки» :-)

Да и то, что исключения заменили ручной раскруткой стека, очень красноречиво показывает насколько они «не дураки».

Открываем первый попавшийся файл: github.com/golang/go/blob/master/src/compress/gzip/gzip.go

На почти 300 строк 12 конструкций вида:
if err != nil {
	return err
}


То есть, более 10% всего кода потрачено на эту тупую копипасту. Примечательно, что ничего другого, кроме как «прокинуть ошибку наверх», тут не делается. И чтобы не заниматься этой ерундой во всех современных языках есть исключения.
Вот не надо про исключения. Вы чистой воды теоретик, это ясно.

У меня были недавно споры по поводу исключений. Человек вот точно также доказывал, что исключение — единственный true метод обработки ошибок, и все обвинения в том, что исключения часто неправильно используются — это потому что «программисты плохие», язык тут не причём. В теории все звучало круто, пока не дошло до практики.
Код этого человека, с обработкой ошибок на исключениях, получил шанс пройти испытание практикой — нужно было изолировать бинарник в докер, и, по ходу, ошибок, выяснять, чего не хватает, порты, связи, файлы — вот это всё. И все эти аргументы разбились в пух и прах — практически ни одна из ошибок не была правильно отрепорчена, метод «выбросить исключение и забить» показал себя на ура — единственным спасение оказался strace. Вопрос — зачем тогда вообще обрабатывать ошибки исключениями, если даже человек, яростно их защищающий и «умеющий правильно их использовать» не может хорошо этим инструментом пользоваться.

Всё, простите, но предлагаю закончить этот спор теоретиков и практиков.
Хотите модных фишечек PLT — есть масса других языков. Go оставьте для продуктивности и практического применения. Точка.
Да-да, ни одной строчки за 20 лет практики так и не написал :-)

Нет уж, сказали А — говорите и Б. Код приложений с их репортами в студию!
Sign up to leave a comment.

Articles

Change theme settings