Pull to refresh

Comments 65

Спасибо за интересную статью, заинтересовался Go.

А я открыл для себя ООП без классов в Lua. Тоже очень красивая по-своему система.
если бы бабушка... писала на Go, как бы изменился мир…

А вообще интересен разбор и анализ межпроцессового обмена данных в Go (не межпотоковый, го-рутинный, а именно межпроцессовый).
Ну как бы любой интерфейс, который вам предоставляет ОС. В случае с Linux это может быть простейший локальный сокет. С Windows я думаю все по сложнее, но тем не менее реализуемо.
Go не любит late binding, но любит messaging, а концепция «свойств и поведения» объектов реализована великолепно, и это дает повод называть Go великолепным ОО-языком.

Что за глупости? Что значит «не любит late binding, но любит messaging»? Интерфейсы и вызов методов конкретных типов через интерфейсы и есть late binding.
Интерфейсы и вызов методов конкретных типов через интерфейсы и есть late binding.

И да, и нет.

Late-binding — это больше о внутренней реализации, и в Go (по крайней мере, в gc-компиляторах: 6g, 8g) используется смешанный вариант — что-то среднее между early-binding и late-binding: таблицы методов для interface- и conrete-типов генерируются на этапе компиляции, а финальная таблица создается на лету, при этом кешируется после первого просчета.
Так что тут в Go mixed-binding ;)
Вот тут подробно расписано: research.swtch.com/interfaces

Касательно messaging — насколько я знаю, Алану Кею были близки идеи CSP, и реализация в Go, наверняка близка, к тому, что он хотел бы увидеть в OO-языках, но я не углублялся, чтобы ненужные холивары не разводить :)
Golang это недо ООП язык.
у ООП есть чёткое определение: инкапсуляция, полиморфизм, наследование.
Golang реализует только часть.
«Статью не читай, сразу комментруй» что ли? Это в первом же абзаце обсуждается.
Пример какой-то не полный. Зачем Person включает Sources и зачем ему FullName, не понятно. Но в целом очень хорошо, спасибо.
Просто чтобы продемонстрировать embedding.
А с примером, да, сумбурно немного — оказалось, не так это просто придумывать красивые, показательные, простые и, одновременно, ёмкие примеры :)
Вообще например такие фичи как embedding и использование в нем не только непосредственно объектов, но и указателей/ссылок на объекты, это одна из тех удивительно простых вещей, про которые думаешь когда впервые узнаешь: ну почему это еще в Си не было сделано, это же так просто и красиво!
func (s TwitterSource) Find(word string)

Все таки receiver лучше через указатель передавать, а то копироваться будет.

func (s *TwitterSource) Find(word string)


Пусть копируется )
Это дешево, и для этого примера не релевантно.
Товарищи минусующие, я, конечно, понимаю, что у вас есть свое представление о дешевизне копирования и я даже готов послушать ваши аргументы, но эта тема поднималась в Go-комьюнити с 2009-го года не раз и ответ на вопрос «использовать поинтер или значение в качестве ресивера» даже вынесено в FAQ — рекомендую познакомиться: golang.org/doc/faq#methods_on_values_or_pointers.

Для тех кому вправду интересно: pointer в качестве ресивера используется если значение наверняка нужно менять или если тип ресивера — большая структура. Для базовых и простых типов, которые не нужно менять — рекомендуется использовать значение. Там есть еще парочка моментов, которые стоит учитывать, но в целом как-то так.

В примере статьи значение менять не нужно и тип очень простой. Минусуйте дальше :)
Правильный краткий ответ на вопрос: «нет, не является, но это не важно» :)
А язык хороший.
Хм, прикольная игрушка на фотке. Сразу же нашел в Украинском магазине и заказал.
Ну вот опередили. Вполне даже очевидное название.
Да, gopher у них удачный очень вышел. Мне больше намного нравится, чем Google-овский — на конференции dotGo в Париже раздавали. Оно такое чувство, будто его камазом сбили и переехали ) А этот крутой. Я его, кстати, подарил девочке из Будапешта, которая движок Prezi писала — она на Lviv IT Arena делала презентацию, как Go в Prezi используется, и это была одна из самых мимимишных презентаций, которые я видел )))

Я, кстати, как-то этой игрушке фотосет знатный устроил :) Вот такие карточки получились:









Некоторые ругают Go, что нельзя
func (s string) Find(word string) ([]Message, error) {...}

а надо
type Plaintext string
func (s Plaintext) Find(word string) ([]Message, error) {...}
Я думаю, это правильное решение. Допустим, разрешили built-in типам добавлять свои методы. Что будет, если чей-то package, включенный в мой код переопределяет метод String() для int? Или два разных package-а, реализуют одинаковый метод для built-in типа. Как такие вещи разруливать?

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

func (s Plaintext) Find(word string) ([]Message, error) {...}

вызвать имплементацию Find у string, если такая имеется?
что-то не нашел в интернетах.
«имплементацию Find у string» — это поиск подстроки, я так понимаю? Операций на строках достаточно много и они вынесены в стандартную библиотеку, в package strings: golang.org/pkg/strings
Find в данном случае вам нужно или Contains (содержит или нет) или Index (позиция, с которой содержит).
не важно, что такое Find. Важно, что мы хотим вызвать одноименный метод.

type AliasOfT T

func (s T) MethodABCD() { .../* надо вызвать это */ ...}

func (s AliasOfT) MethodABCD() { super.MethodABCD() }


Что нужно написать вместо super.MethodABCD(), чтобы вызвать MethodABCD() для T?
Если мы вызовем просто MethodABCD(), мы рекурсивно закрутимся.
Приведите к нужному типу и вызывайте: T(s).MethodABCD()

Но вообще немного weird дизайн. Я такого использования не встречал, это калька с class-oriented языков.
Спасибо.

Давеча boltdb заюзывал и надо было *DB разными методами разукрасить, специфичными для проекта.
Мне так понадобилось метод (… *DB) Close() «переопределить».

В итоге, я не догадался, и выкрутился контейнером над *DB. Оказалось потом не зря.
В Go используется правило регистра первой буквы имени — если название начинается заглавной буквы — это public-доступ, если со строчной — private. Вначале может показаться посягательством на свободу слова неудобством, но с первых строк на Go, понимаешь, насколько это было удобное решение.

… и если ты хочешь сделать публичный метод приватным или приватный публичным — нужно пройтись по всему проекту и сменить регистр. Оооок, очень удобно. И правильно, нечего без IDE программировать на серьёзных языках.
Ну да, рефакторить код лучше не ручками :) GoRename конкретно этот use-case делает очень простым.
Изобрели проблему и героически её побороли. Оооок.
Если вы чаще, чем раз в месяц меняете туда сюда видимость методов, то, наверное да, Go вам не подходит. А если еще и «я так не привык» для вас затмевает все остальные преимущества, которые дает Go (и которые даже не затрагивались в этой статье) — то тем более )
Очевидно, сам Go здесь ни при чём — это разработчики на Go придумали такую, кхм, странную концепцию. А Двораком не пользуетесь?
Извините, что игнорирую ваш сарказм, но расскажу один момент.

Когда я только познакомился с Go, самым странным для меня было то, что компилятор выдавал ошибку на неиспользованный импорт или неиспользованную переменную. И, более того, изменить это поведение нельзя было — ну там, какой-то опцией командной строки например.
WTF, подумал я — что за ограничение свободы слова и посягательство на базовые права человека? Да какое право они имеют мне диктовать, оставлять или нет неиспользованные импорты? Хочу и оставляю, я свободный человек! Да и вообще, это же офигеть, как неудобно. Что это они вообще придумали, идиоты. Я то знаю как нужно — я же привык как в других языках.

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

Роб Пайк, кстати, рассказывал подробно по всем дизайнерским решениям языка — что за этим стояло, сколько месяцев или лет они обсуждали все возможные решения и почему пришли именно к такому. У меня есть огромное доверие к Кену и Робу — думаю, их опыт в программировании и работы с разными программистами и командами больше моего в огромное количество раз, и когда мне вначале, еще не испробованная фича кажется «неправильной» — скорее всего, я просто сноб и боюсь изменить свои убеждения. Но хорошо, что я не боюсь :) Те вещи, за которые я бы раньше на C++ или Python даже не взялся, сейчас я пишу за пару дней. Чего и вам желаю. :)
Ну, тогда еще не было goimports :)
Кстати, я им немного попользовался и вернулся обратно на go fmt (при сохранении всмісле) — во-первых, как-то уже привык добавлять импорты сам перед тем, как использовать какие-то вещи из них — не добавив, банально автодополнение не сработает, а удалять через :GoDrop легче простого, не сходя с нужной строки. А во-вторых, там есть баг, до которого у меня руки не дошли разобраться — но imports в некоторых случаях, неверно угадывает путь и вставляет неправльные import-ы.
Я его использую совместно с go fmt.
Насчет бага — да столкнулся.
Зачем совместно? goimports ведет себя также, как и gofmt + исправляет импорты.
Концепция кстати вполне интуитивная. Немного контринтуитивно, что вот так например нельзя.
func foo() 
{...
}
Это другая тема, это про точки с запятой. Опять же, это все в спецификации, которая читается целиком за час.

Формально, в Go используются точки с запятой, но они не обязательны — лексер их сам расставляет, руководствуясь простыми правилами. В данном примере лексер ставит точку с запятой после ')' — поэтому и ошибка. Этот пример, кстати, там же, в спеке языка :)
golang.org/doc/effective%5Fgo.html#semicolons
Спасибо, читал:)) Все же
-встроенные типы особенней самодельных
-встроенный sort() может быть полиморфным, а самодельный нет
-на вопрос «When generics?» Rob Pike отвечает «We are done.» и представляет go generate
это все прекрасно аргументировано и с этим можно нормально работать и Pike — commander и голова. Но расстановка семиколонок и навязанный стандарт gofmt — для меня слишком proprietary and opinionated. Go сейчас мой любимый язык, более того Plan9 acme by Rob Pike — мой рабочий редактор, однако диктат отцов основателей местами чрезмерный что ли.
Согласен, что встроенные типы особым статусом наделены, но неспроста же.
Насчет generics и «commander и голова» — ну, это же не «Пайк единолично не хочет generics» :) Хочет же, хоть и неохотно ) Но не видит пока-что способа действительно красиво это сделать.
Я лично пока-что не понимаю, какой есть use-case, когда необходим generics. Те примеры, которые я видел решаются интерфейсами, а иногда через reflection (что не слишком красиво, согласен). Я продолжаю искать в реальных задачах способ наткнутся на ситуацию, когда упрусь в необходимость generics — но пока что этого не случалось, и адекватных ответов по этой теме я не видел. Люди, как правило, просто хотят решать задачу тем же путем, которым они привыкли в прошлых языках.

Acme гляну, интересно )
Reflection'ы же медленные, и с кешированием код получается несколько громоздким.
Согласен. Но все равно хочу увидеть задачу, в которой Go by design без дженерикс не справляется.
Вот, к примеру, хорошая реализация B-Tree, которая зачастую в других языках решается с помощью generics. По-моему, очень красиво и элегантно:
godoc.org/bitbucket.org/santucco/btree
Я лично пока-что не понимаю, какой есть use-case, когда необходим generics.

func Sort(func compare(a,b T) bool, []T) []T
func Filter(func fine(a T) bool, []T) []T
func Map(func apply(a T) Ta, []T) []Ta
func Fold(func combine(a, b T) Tc, []T) Tc

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


Посмотрите, как реализован stdlib-овский sort, или приведенный выше B-Tree.
Это просто другой подход к проблеме. Можно спорить о том, какие минусы и какие плюсы этого подхода, и даже спорить — а считать ли этот подходит тоже как вид generics, но уж точно на «Go тут фейлится» не подходит.
Ну и не убедительные примеры
  • stdlib sort использует reflection, что медленно и велосипед
  • godoc.org/bitbucket.org/santucco/btree построен на encoding/binary, то есть не typesafe, а скорее untyped
а почему не реализовать полноценный Хиндли-Милнер в компиляторе? В Rust же реализован. Я так понимаю Pike декларировал абстрактный механизм препроцессора go generate и колбасьте что хотите, а компилятор должен быть быстрым))
stdlib sort использует reflection, что медленно и велосипед

В каком месте и для чего sort использует reflection? Желательно прямо строчку кода укажите :)

godoc.org/bitbucket.org/santucco/btree построен на encoding/binary, то есть не typesafe, а скорее untyped

нет, encoding/binary используется исключительно для чтения/записи в с storage (io.ReadWriter) — это задача сериализации/десереализации данных. Сам же b-tree алгоритм построен исключительно на использовании интерфейсов.

Поймите, generics — это написание алгоритмов в type-независимой манере. Go дает возможность это делать с помощью интерфейсов — ваш тип должен реализовать методы, необходимые для работы алгоритма — и наслаждайтесь. В С++ эту проблему решили иначе — темплейтами, в Java — еще иначе — и везде за счет потерь в скорости, читаемости и/или эффективности. И люди, привыкшие к тем или иным решениям, просто чувствуют себя без них неудобно в новом языке — отсюда и эти «Пайк, спаси, введи темплейты». Но это неправильно.

Или вот еще — когда для каких-то особых случаев людям советуют использовать reflect или interface{} с кастингом — они начинают рассказывать про «это медленно» и вообще, мы хоть это за zero costs получить. Блин, конечно медленно — а в в других языках, generics, думаете бесплатно получается?
В конце-концов да, тем кто прямо без темплейтов жить не может — (уже почти) есть go generate и были какие-то ужасные (как по мне) решения вроде gonerics.

Вобщем я все еще в поиске реальных use-кейсов, где Go без имплементации C++/Java-style generics не справляется красиво.
А насчет Хиндли-Милнер — не знаю, и мне сложно проанализировать, поскольку с языками его использующего не работал и не сильно понимаю ценность.
Хиндли-Милнер нужен, чтобы
//имея
func Btree(node T) Btree T {...}
//написать
var bt Btree int 
//так же легко как
var sl [] int
//
//и typesafe
type T; //type parameter
func twotime([]T) []T {...}
//или
func twotime(bt Btree T) bt Btree T {...}
//вместо untyped
func twotime(sl []interface{}) []interface{} {...}

C примерами кстати убедили, невнимательно исходники читал, виноват))
Вот тут Russ Cox рассказывает про Hindley-Milner, почему это не есть так гуд, как это кажется в теории:
To take one example, Hindley-Milner type inference is very well understood and I think generally accepted in the programming language community as a good feature. But new ML programmers inevitably hit the situation where changing one part of a program causes a mysterious type error in a seemingly unrelated part. The solution is that in practice one writes type signatures for most functions. Hindley-Milner type inference is beautiful research, but it’s not as beautiful in practice. In Go, the only type inference is that if you say var x = e, the type of x comes from e. This rule is simple and predictable and doesn’t suffer from the kinds of “spooky action at a distance” of more complex type inference algorithms. I miss having more complex inference sometimes, but Go’s simple rule handles at least 90% of the cases you care about, with none of the unpredictability or mystery.
Но расстановка семиколонок и навязанный стандарт gofmt — для меня слишком proprietary and opinionated.

А по-моему, gofmt — одна из самых замечательных вещей в Go. Никаких тебе больше code style guides. Никаких споров о tabs vs spaces и о размерах отступов. Не надо больше думать, переносить фигурную скобку на следующую строчку или нет.
Весь код единообразен и глаз быстро привыкает именно к такому форматированию, что крайне положительно сказывается на легкости чтения.
Подпишусь. Сколько нервов и споров уходило в других языках на «как надо расставлять скобочки», и сколько на маты, когда смотришь код людей, которые класть хотели на форматирование.
В Go в итоге 99.9% кода гарантированно читабельно и аккуратно.
В Python похожая реализация, private поля начинаются с двух подчеркиваний. Дело привычки.
Что? В какой момент в питоне появились приватные поля? Не вводите людей в заблуждение. Во-первых, в большинстве проектов «приватные» поля принято начинать с одного нижнего подчеркивая, а не с двух. Во-вторых, два нижних подчеркивания стараются использовать только для служебных «магических» методов вроде __len__(). И в-третьих, и те и другие все равно можно изменять, просто с двумя подчеркиваниями добавляется имя класса (что кстати ломает нормальное использование в дочерних классах).
Вы не правы. 1) С одного подчеркивания обычно называются «внутренние» поля, которые не нужны клиенту, но могут быть нужны наследникам. По идеологии тут ближайшее — protected поле, хотя это только соглашение имен, защиты на уровне языка нет.

2) Использование двух подчеркиваний ведет к автоматическому переименованию поля к виду _<classname>__<fieldname>, таким образом исключается возможность обратиться к полю родителя, не используя интроспекции (так как self.__field у дочернего класса приведет к обращению к self._child__field).
Извне пользоваться прямым именем, начинающимся с __ также запрещено.
Таким образом, полем в явном виде можно пользоваться только внутри класса. Такое поведение делает такие поля приватными.

3) Магические методы имеют имя __<имя>__, приватные не имеют двух подчеркиваний в конце.

см. PEP8 (__double_leading_underscore)
По поводу пункта 2. Создайте класс Foo и определите поле self.__bar в __init__(). Создайте foo = Foo() а потом спокойно присвойте foo._Foo__bar любому значению. Все прекрасно работает без всякой интроспекции (кстати, что такое интроспекция в питоне? гм, getattr() с __dict__-ом чтоли?). Так что ничего они не приватные. Приватность — это невозможность изменить поле извне, в т.ч. в дочерних классах. И раз уж об этом зашла речь, окей, в том числе для этого придумали __slots__.

PEP8 это, конечно, здорово, но в реальном мире, в котором написаны к примеру matplotlib, twisted и tornado никто никогда в здравом уме такую свинью с двумя подчеркиваниями разработчикам не подкладывает. Можете привести пример известного, крупного проекта, где «приватные» поля начинаются как пепе?
Хотя нет, даже возня со __slots__ не поможет. Нельзя добиться приватности в Питоне, не тот язык)
> foo._Foo__bar
Это не прямое обращение по имени, а скорее хак. Да, это не интроспекция, согласен.

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

Ну вот специально поискал по OpenStack'у, нашел кучу примеров использования. У вас какое-то предубеждение к этому) Вполне нормальный механизм.
Sign up to leave a comment.

Articles

Change theme settings