All streams
Search
Write a publication
Pull to refresh
0
@flatscoderead⁠-⁠only

User

Send message
На фоне этих проблем на ровном месте случай с «Говорю как человек, вынужденный жить с… ресиверами только this ...» выглядит не так уж и плохо.
gorename'ом переименовывать каждый ресивер

А если новое имя конфликтует с переменными в функции, то еще и их нужно переименовать.
А если вы делаете рефакторинг, например, «AsyncProducer» переименовываете в «AsyncFactory», имена ресиверов тоже автоматически изменяются или это надо делать вручную?
Но в runtime работает ducktyping

Я, кстати, про runtime изначально ничего не говорил.

В Go главным образом используются проверки на этапе компиляции. Посмотрел, в Wikipedia даже есть пояснение на этот счет:

Duck typing is similar to, but distinct from structural typing. Structural typing is a static typing system that determines type compatibility and equivalence by a type's structure, whereas duck typing is dynamic and determines type compatibility by only that part of a type's structure that is accessed during run time.

The OCaml, Scala, Go, Elm, and Gosu languages support structural typing to varying degrees.
Go использует структурную типизацию по методам для определения совместимости типа и интерфейса.
Вы пытаетесь обойти duck typing

Причем здесь Duck typing?

Речь идет о Structural type system против Nominal type system.

См., например, Nominative And Structural Typing.

Which is better? There are advantages to both approaches. Structural typing is arguably more flexible — one common complaint in JavaLanguage… On the other hand, it is very common that two objects that are structurally equivalent are semantically different...

Вот о чем я говорю.
Если класс реализует нужный вам интерфейс правильно — то неважно: хотели ли его реализовать или «так случайно получилось».

На самом деле это важно. Реализация интерфейса предполагает выполнение заявленного контракта по этому интерфейсу.

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

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

Где-то пишут так:

func (s SpReg) String() string {
	return fmt.Sprintf("SpReg(%d)", int(s))
}


а где-то так:

func (r PCRel) String() string {
	return fmt.Sprintf("PC%+#x", int32(r))
}


Но очевидно, что такое правило приводит еще к паре неприятных случаев.

Первый — это использование «i» в качестве ресивера, переменной, которая обычно применяется в циклах.

func (i Imm) String() string {
	return fmt.Sprintf("%d", int32(i))
}


Второе — это использование «l» в качестве идентификатора. За такое нужно бить сильно и больно по пальцам (за сходство с цифрой 1):

func (l Label) String() string {
	return fmt.Sprintf("%#x", uint32(l))
}


Кстати, в Питоне похожая ситуация с возможностью именования. Однако, так на этот счет есть рекомендация:

...Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention...

См. The Python Tutorial: 9.4. Random Remarks
Мне лично имена ресиверов помогают — они сохраняют контекст внутри кода. То есть я могу открыть любой файл в любом месте и читать код

Вот тут вы что имеете в виду?

И это касается только вашего кода или вообще любого кода на Go?
У этого объекта, в отличие от других доступы приватные поля, например. По отношению к этому объекту нарушается (может не использоваться или не подходит) инкапсуляция.
Я не говорю, что это проблеме нерешаемая. Я хочу сказать, что этой проблемы в принципе могло не быть.

Сделали, ведь, gofmt — решили проблему на канонический формат кода. Могли бы и не делать gofmt, тоже решали бы эту проблему (правда, каждый по-своему).
Типичный случай: есть тип и нужно выяснить, реализует от какой-то интерфейс или нет.

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

Ну выше JekaMas упомянул конфликт/спор.

Не скажу, что от свободного именования ресивера только минусы. Естественно, есть и плюсы.

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

А не отмотав к сигнатуре как вы узнаете название ресивера?
По п.1.

Ну есть разные варианты, в т.ч. явно в комментариях каким-либо образом описать свои намерения. Но в том же Java, например эта проблема решена на уровне языка.

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

Может быть частично проблему решает и упомянутый Oracle.

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

Вы выше написали: «что нам в рабочем code style напридумывали люди с полугодом опыта в golang, но зато с десятком лет в java, c или perl».

В моем понимании, это следствие того, что в java, с++, js этой свободы нет (и не надо) и программисту не нужно задумываться, как назвать receiver. Обычно при написании кода есть, над чем подумать более полезном. А задумываться, как называть receiver — это трата времени и снижение эффективности.

В Go ведь сделали штатный форматер кода? Да, это отлично, вероятность различия формата минимизирована.

Так же есть рекомендации по названию интерфейсов, например. Тоже хорошо.

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

1. Все же отсутствие возможности (необходимости) задекларировать реализуемый интерфейс ухудшает естественную документируемость кода. Это важно при чтении кода.

2. Возможность произвольно назвать receiver в методе (это, например, «v» в func (v Vertex) Abs() float64 ..) по факту приводит к разнобою в именовании этих receiver-переменных в разных местах. По-моему, в этом месте дана ненужная свобода. Лучше бы чего-то зарезервировали или советовали бы использовать одинаковое название. Сейчас имеется разнобой и необходимость задумываться, а как назвать эту переменную — лишняя трата времени.

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

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

хочу как в питоне

Про питон особо не знаю, скорее как в JavaScript.

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

Т.е. что бы идентификатор _<...> — был приватным, а любой другой — публичным. Ну и тогда название функций можно было бы писать со строчной буквы.

Иными словами, вместо

type mytype struct {
    size string
    hash uint32
    Data []byte
}


Хотелось бы видеть:

type _mytype struct {
    _size string
    _hash uint32
    data  []byte
}


Кто-то есть с таким же мнением?

Information

Rating
Does not participate
Registered
Activity