не могу вешать никаких ярлыков, но вы живете в слишком прекрасной реальности
Но таки повесили ярлык :) Нет, я не питаю иллюзий и прекрасно понимаю, что это происходит время от времени, но давайте будем откровенны — бОльшая часть библиотек в Go все-таки уважают принцип обратной совместимости, и, насколько это возможно, стремятся не ломать API. Или же — пишут большими жирными буквами — «unstable. possible API break in the future» или что-то вроде.
И это я говорю о вообще любых библиотеках, мелких и никем не используемых, в том числе. Самые же основные, популярные вещи (вроде gorilla или mysql/mgo/amqp-драйверов) — стабильны, и ломать в них API не будут, 100%.
Вобщем, я не отрицаю надобность dependency менеджмента, но стараюсь не использовать. В приватных проектах, где GOPATH шерится с другими разработчиками — время от времени обновляю зависимости с помощью Go-Package-Store, и это да — можно называть «GOPATH per project», но project тут в общем смысле — на самом деле там много go-программ и все используют один общий GOPATH.
И да, я не готов спорить сейчас по теме version/dependency-менеджмента, поскольку все варианты не пробовал, но, насколько я понимаю, все эти решения создают отдельную поддиректорию вроде _vendor или .godeps и в ней хранят все зависимости, и её же используют как (второй) GOPATH. При этом делается это автоматически, без форсирования использовать какую-то нестандартную схему GOPATH.
Если кто-то напишет подробный пост с различиями существующих решений — будет отлично и познавательно.
Как уже написали — вы можете «в любой произвольной папке создать и скомпилить программу на go».
GOPATH — это что-то вроде PYTHON_PATH, переменная, указывающая, где лежат все ваши библиотеки и исходники. То, что ваш код удобнее складывать тоже в GOPATH/src — это не обязательное требование, но естественно возникающее удобство, как только вы начинаете писать код, который а) выкладывается в open-source б) начинается использоваться в других ваших или не ваших проектах. Тогда просто нет разницы — делаете вы go get драйверу mongo или своей библиотеке.
И на самом деле GOPATH очень помогает, причем неявно, организовывать ваш код. Это просто и это удобно. С этой схемой сложно создавать хаос из исходников на компьютере. Попробуйте, и через какое-то время вы поймете, насколько это удобно. Вот тут, к примеру, один девелопер, вообще, говорит, что взял идею GOPATH на вооружение не только go-кода, но и для всего другого: mwholt.blogspot.com.es/2014/02/why-i-use-just-one-gopath.html
Окей, с вашим примером, на самом деле, два момента:
1) отдельный GOPATH для отдельного проекта
2) исходники в корне GOPATH, видимо, для использования относительных путей для «модулей»
Изолированный GOPATH — с зависимостями (как с применением менеджеров пакетов, так и без) — вобщем-то, не крамольно, и для «локинга» зависимостей — даже необходимо. Но это вполне ложится на исполнение менеджерами пакетов, не обязательно выделять отдельный GOPATH для каждого проекта. Я на самом деле стараюсь не пользоваться этими менеджерами, и поддерживать код с последними версиями всех зависимых библиотек (Go-Package-Store очень помогает). Но пока еще ни разу не пришлось, собственно, «поддерживать» — все таки «обещание backward-compatibility» достаточно хорошо и на авторов библиотек распространяется, создает некую культуру.
Ну а второй момент — с выходом за src/ и относительными путями — это уж точно неодобрительная практика.
В конце концов, если вы захотите завтра ваш проект заопенсорсить, и выложить на github — придется всю эту конструкцию с отдельным GOPATH и относительными именами рефакторить и менять под корень.
Почему? Это заезженная тема, по ней куча статей и топиков на golang-nuts/reddit и общая позиция (в том числе подкрепляемая официальными заявлениями Go-команды), в том, что single GOPATH — это официально поддерживаемый метод, а остальные способы активно не одобряются.
Ну, в base64 засовывать — это не очень продуктивно в общем случае. Лучше таки или go-bindata (+go-bindata-assetfs) или go-rice использовать.Тем более, что c go generate их еще удобнее стало использовать.
В 90% случаев вы один раз в жизни устанавливаете свой рабочий workspace — GOPATH=C:\Projects\GO (в вашем примере) и работаете в этой директории. К примеру, ваши проекты могут быть в:
C:\Projects\GO\src\test\mytestprj\
C:\Projects\GO\src\github.com\paco\mycoolprj\
Можно писать код и вне GOPATH, но все сторонние пакеты которые вы будете использовать (и ставить через go get) — будут сохраняться именно в GOPATH\src и оттуда браться компилятором.
В src хранятся исходные коды. В pkg — скомпилированные бинарные версии пакетов, которые предназначены для линковки (библиотеки, другими словами). В bin — скомпилированные файлы для исполнения (бинарники). $GOPATH/bin удобно добавить в PATH, потому как тогда легко инсталлировать внешние Go-программы (ту же go-bindata) одним телодвижением — go install — и оно готово к использованию в вашей системе.
Возможно, но в пользу решения в Go есть два момента:
1) (объективный) — авторы Go прекрасно ознакомлены с проблемами созданными checked exceptions, и об этом в одной из статей по ссылкам выше написано, У меня есть огромный кредит доверия этим товарищам, которые, в конце-концов, написали Unix, поэтому я предполагаю, что они знают, о чем говорят.
2) (субъективный) — я вижу изменения по себе. До Go я считал обработку ошибок чем-то второстепенным и опциональным, отвлекающим и мешающим. В языках с исключениями соблазн «просто-выкинуть-исключение-и-забыть» был столь высок, что я это делал везде. Да, можно сколько угодно говорить, что я просто плохой программист и мне нужно было прочитать пару толстых книжек и научиться правильно работать с исключениями — но это то, что было в реальном мире — люди идут простым путем, и я не исключение. И вот только с Go мне это осознание пришло естественным путем, просто потому-что язык так сделан, и не случайно, а намеренно для этого.
Из перечисленного, скорее, «Развитие IT индустрии». Go не «представляет» какой-то уникальный метод обработки ошибок — но дизайн этого аспекта (под которым подразумевается не только наличие и форма определенных фич, но и отсутствие других) создает стимул для более внимательной обработки ошибок и стимулирует в головах программистов переход от «ошибки — это что-то опциональное» до «всегда проверять/хендлить ошибки».
А стимул умноженный на миллионы человеко-часов приведет к более качественной обработке ошибок в целом, к меньшему количеству сбоев и к повышению культуры программирования в сумме.
Ну, я там смайлик специально поставил, чтобы понятно было, что это как-бы шутка )
Но теперь ваша версия звучит более удачно — один метод более эффективный(«правильный») в долгосрочной перспективе, чем другой.
Но это не то же самое, что метафора «серебрянная пуля», согласитесь.
Но вообще этот вопрос настолько важен, что мне кажется в статье про «серебряную пулю для обработки ошибок» нельзя это не упомянуть. Это я, разумеется, не Вам, а автору статьи.
Только увидел ваш комментарий. Я, вероятно, еще не научился доносить ясно мысль, но называть статью статьей «про серебрянную пулю для обработки ошибок» я считаю оскорблением :)
Статья абсолютно не о том, что тот или иной метод — серебрянная пуля. И в комментариях выше я явно дал понять, что да, есть частные случаи, когда краткосрочная выгода от использования исключений больше. Но её недостаточно, чтобы перекрыть долгосрочные недостатки.
Отвечаю на вопрос — возможно можно было придумать лучший заголовок для статьи, но мне хотелось раскрыть именно этот аспект — который безусловно является преимуществом языка — но который часто не попадает в списки «плюшек и преимуществ языка».
Речь о том, что дизайн языка способствует использованию «правильных» вещей в разработке ПО. «На другом языке тоже можно написать что-то похожее с похожим эффектом» — это совсем другое. Никто не будет «писать что-то похожее», если можно сделать проще («выбросить исключение» или проигнорировать ошибку). Go вынуждает программистов быть более внимательными к обработке ошибок — вне зависимости от их уровня мастерства и религиозных убеждений.
Два момента:
— если вы на 150% уверены, что не хотите проверять ошибки для Atoi() — можно а) использовать свой двухстрочный враппер б) использовать сторонние пакаджи, с врапперами для Atoi/ParseInt.
— в долгосрочной перспективе допущение «это пришло из внутренних ресурсов, поэтому можно не проверять» — плохое. За исключением комментариев, у вас нет возможности нигде это допущение проверить или сформулировать, и когда ваш код начнет развиваться/рефакториться и с ним будут работать другие люди — они не будут знать про это допущение, даже когда «внутренний инпут» заменится на «данные от клиента». а это источник багов. именно поэтому лучше приучить себя лишний раз проверять даже те ошибки, которые сейчас кажутся невозможными.
Это эмоциональное, не хочу никого задеть, простите. Просто практически все программы на Java, с которыми мне приходилось работать за последние 10 лет — были символом тормознутости, неудобства и глючности. Может, конечно, просто совпадение, но мне хватило IBM Lotus Notes, Openproj и Amazon EC2 CommandLine Client чтобы при слове «написано на Java» начинало подташнивать.
Про адский ад с зависимостями и установкой/апгрейдом нужной версии — молчу.
Единственная программа на Java, которая более менее меня радовала (хоть и жрала всю возможную память, но там специфика позволяла) — это agent-based симулятор Gama.
Да, этот пример тут проскакивал уже.
Rust-овский Result — по сути дела лишь union, в котором может храниться либо значение, либо ошибка. Это выглядит симпатично, хотя «явность наличия ошибки» тут заключается, если я правильно понимаю, лишь в том, что File::open() возвращает IoResult, в котором явно указан взаимоисключащий тип IoError. Если убрать «плюс» union в экономии места, то это ничем не отличается от возврата структуры с двумя полями, вместо двух значений, как в Go. И вот здесь «возврат двух значений» как раз явнее, потому что со вторым значением что-то таки нужно делать, а поле в структуре (точнее в union) можно молча «забыть».
Сумбурно, но как-то так.
Rust, конечно, интересный язык, и я стараюсь на него посматривать иногда, но, да простят меня фанаты Rust, пока что очень отталкивает своей непродуманностью. Код, который чуть ли не с нуля переписывают несколько раз подряд, в котором сначала добавляют огромное количество фич, которые потом же начисто выпиливают — не внушает ни доверия, ни желания его изучать. Высокий порог входа и достаточно вырвиглазный синтаксис тоже не добавляют доверия.
Но следить за развитием, конечно, интересно — ниша все-таки важная и амбиции большие.
Собственно об этом и статья. Ошибку можно спрятать, в некое подобие union и давать читать либо переменную, либо ошибку, а можно выбрасывать исключение. Но в Go принято ошибку возвращать *явно* — от этого «не заметить» ошибку становится невозможно — что порождает стимул всегда обрабатывать ошибки, где они могут возникать. В этом и поинт.
Но таки повесили ярлык :) Нет, я не питаю иллюзий и прекрасно понимаю, что это происходит время от времени, но давайте будем откровенны — бОльшая часть библиотек в Go все-таки уважают принцип обратной совместимости, и, насколько это возможно, стремятся не ломать API. Или же — пишут большими жирными буквами — «unstable. possible API break in the future» или что-то вроде.
И это я говорю о вообще любых библиотеках, мелких и никем не используемых, в том числе. Самые же основные, популярные вещи (вроде gorilla или mysql/mgo/amqp-драйверов) — стабильны, и ломать в них API не будут, 100%.
Вобщем, я не отрицаю надобность dependency менеджмента, но стараюсь не использовать. В приватных проектах, где GOPATH шерится с другими разработчиками — время от времени обновляю зависимости с помощью Go-Package-Store, и это да — можно называть «GOPATH per project», но project тут в общем смысле — на самом деле там много go-программ и все используют один общий GOPATH.
И да, я не готов спорить сейчас по теме version/dependency-менеджмента, поскольку все варианты не пробовал, но, насколько я понимаю, все эти решения создают отдельную поддиректорию вроде _vendor или .godeps и в ней хранят все зависимости, и её же используют как (второй) GOPATH. При этом делается это автоматически, без форсирования использовать какую-то нестандартную схему GOPATH.
Если кто-то напишет подробный пост с различиями существующих решений — будет отлично и познавательно.
GOPATH — это что-то вроде PYTHON_PATH, переменная, указывающая, где лежат все ваши библиотеки и исходники. То, что ваш код удобнее складывать тоже в GOPATH/src — это не обязательное требование, но естественно возникающее удобство, как только вы начинаете писать код, который а) выкладывается в open-source б) начинается использоваться в других ваших или не ваших проектах. Тогда просто нет разницы — делаете вы go get драйверу mongo или своей библиотеке.
И на самом деле GOPATH очень помогает, причем неявно, организовывать ваш код. Это просто и это удобно. С этой схемой сложно создавать хаос из исходников на компьютере. Попробуйте, и через какое-то время вы поймете, насколько это удобно. Вот тут, к примеру, один девелопер, вообще, говорит, что взял идею GOPATH на вооружение не только go-кода, но и для всего другого: mwholt.blogspot.com.es/2014/02/why-i-use-just-one-gopath.html
1) отдельный GOPATH для отдельного проекта
2) исходники в корне GOPATH, видимо, для использования относительных путей для «модулей»
Изолированный GOPATH — с зависимостями (как с применением менеджеров пакетов, так и без) — вобщем-то, не крамольно, и для «локинга» зависимостей — даже необходимо. Но это вполне ложится на исполнение менеджерами пакетов, не обязательно выделять отдельный GOPATH для каждого проекта. Я на самом деле стараюсь не пользоваться этими менеджерами, и поддерживать код с последними версиями всех зависимых библиотек (Go-Package-Store очень помогает). Но пока еще ни разу не пришлось, собственно, «поддерживать» — все таки «обещание backward-compatibility» достаточно хорошо и на авторов библиотек распространяется, создает некую культуру.
Ну а второй момент — с выходом за src/ и относительными путями — это уж точно неодобрительная практика.
В конце концов, если вы захотите завтра ваш проект заопенсорсить, и выложить на github — придется всю эту конструкцию с отдельным GOPATH и относительными именами рефакторить и менять под корень.
Ну, в base64 засовывать — это не очень продуктивно в общем случае. Лучше таки или go-bindata (+go-bindata-assetfs) или go-rice использовать.Тем более, что c go generate их еще удобнее стало использовать.
В 90% случаев вы один раз в жизни устанавливаете свой рабочий workspace — GOPATH=C:\Projects\GO (в вашем примере) и работаете в этой директории. К примеру, ваши проекты могут быть в:
C:\Projects\GO\src\test\mytestprj\
C:\Projects\GO\src\github.com\paco\mycoolprj\
Можно писать код и вне GOPATH, но все сторонние пакеты которые вы будете использовать (и ставить через go get) — будут сохраняться именно в GOPATH\src и оттуда браться компилятором.
В src хранятся исходные коды. В pkg — скомпилированные бинарные версии пакетов, которые предназначены для линковки (библиотеки, другими словами). В bin — скомпилированные файлы для исполнения (бинарники). $GOPATH/bin удобно добавить в PATH, потому как тогда легко инсталлировать внешние Go-программы (ту же go-bindata) одним телодвижением — go install — и оно готово к использованию в вашей системе.
1) (объективный) — авторы Go прекрасно ознакомлены с проблемами созданными checked exceptions, и об этом в одной из статей по ссылкам выше написано, У меня есть огромный кредит доверия этим товарищам, которые, в конце-концов, написали Unix, поэтому я предполагаю, что они знают, о чем говорят.
2) (субъективный) — я вижу изменения по себе. До Go я считал обработку ошибок чем-то второстепенным и опциональным, отвлекающим и мешающим. В языках с исключениями соблазн «просто-выкинуть-исключение-и-забыть» был столь высок, что я это делал везде. Да, можно сколько угодно говорить, что я просто плохой программист и мне нужно было прочитать пару толстых книжек и научиться правильно работать с исключениями — но это то, что было в реальном мире — люди идут простым путем, и я не исключение. И вот только с Go мне это осознание пришло естественным путем, просто потому-что язык так сделан, и не случайно, а намеренно для этого.
А стимул умноженный на миллионы человеко-часов приведет к более качественной обработке ошибок в целом, к меньшему количеству сбоев и к повышению культуры программирования в сумме.
Но теперь ваша версия звучит более удачно — один метод более эффективный(«правильный») в долгосрочной перспективе, чем другой.
Но это не то же самое, что метафора «серебрянная пуля», согласитесь.
Только увидел ваш комментарий. Я, вероятно, еще не научился доносить ясно мысль, но называть статью статьей «про серебрянную пулю для обработки ошибок» я считаю оскорблением :)
Статья абсолютно не о том, что тот или иной метод — серебрянная пуля. И в комментариях выше я явно дал понять, что да, есть частные случаи, когда краткосрочная выгода от использования исключений больше. Но её недостаточно, чтобы перекрыть долгосрочные недостатки.
— если вы на 150% уверены, что не хотите проверять ошибки для Atoi() — можно а) использовать свой двухстрочный враппер б) использовать сторонние пакаджи, с врапперами для Atoi/ParseInt.
— в долгосрочной перспективе допущение «это пришло из внутренних ресурсов, поэтому можно не проверять» — плохое. За исключением комментариев, у вас нет возможности нигде это допущение проверить или сформулировать, и когда ваш код начнет развиваться/рефакториться и с ним будут работать другие люди — они не будут знать про это допущение, даже когда «внутренний инпут» заменится на «данные от клиента». а это источник багов. именно поэтому лучше приучить себя лишний раз проверять даже те ошибки, которые сейчас кажутся невозможными.
Это эмоциональное, не хочу никого задеть, простите. Просто практически все программы на Java, с которыми мне приходилось работать за последние 10 лет — были символом тормознутости, неудобства и глючности. Может, конечно, просто совпадение, но мне хватило IBM Lotus Notes, Openproj и Amazon EC2 CommandLine Client чтобы при слове «написано на Java» начинало подташнивать.
Про адский ад с зависимостями и установкой/апгрейдом нужной версии — молчу.
Единственная программа на Java, которая более менее меня радовала (хоть и жрала всю возможную память, но там специфика позволяла) — это agent-based симулятор Gama.
Rust-овский Result — по сути дела лишь union, в котором может храниться либо значение, либо ошибка. Это выглядит симпатично, хотя «явность наличия ошибки» тут заключается, если я правильно понимаю, лишь в том, что File::open() возвращает IoResult, в котором явно указан взаимоисключащий тип IoError. Если убрать «плюс» union в экономии места, то это ничем не отличается от возврата структуры с двумя полями, вместо двух значений, как в Go. И вот здесь «возврат двух значений» как раз явнее, потому что со вторым значением что-то таки нужно делать, а поле в структуре (точнее в union) можно молча «забыть».
Сумбурно, но как-то так.
Rust, конечно, интересный язык, и я стараюсь на него посматривать иногда, но, да простят меня фанаты Rust, пока что очень отталкивает своей непродуманностью. Код, который чуть ли не с нуля переписывают несколько раз подряд, в котором сначала добавляют огромное количество фич, которые потом же начисто выпиливают — не внушает ни доверия, ни желания его изучать. Высокий порог входа и достаточно вырвиглазный синтаксис тоже не добавляют доверия.
Но следить за развитием, конечно, интересно — ниша все-таки важная и амбиции большие.