«Ошибки — это значения» в Go и эхо VB

от автора

Судьба завела меня (программиста практика, в основном использующего C#) на проект, в котором основной функционал разрабатывается на Go.
Изучая Go обратил внимание на непривычную практику обработки ошибок. Почитав разъяснения в статьях Ошибки — это значения и в Почему «ошибки это значения» в Go отметил, что предлагаемые там решения заставляют вспомнить одну особенность Visual Basic, которую очень не лестно комментировали программисты.

Суть в следующем. Существуют жалобы программистов про проверку ошибок в Go. Возьмём пример из статьи Ошибки — это значения:

_, err = fd.Write(p0[a:b]) if err != nil {     return err } _, err = fd.Write(p1[c:d]) if err != nil {     return err } _, err = fd.Write(p2[e:f]) if err != nil {     return err } // and so on 

Мы видим, что присутствует повторяющий код, связанный с обработкой ошибок, который и вызывает нарекания. Вроде бы, почему бы не использовать стандартную практику try-catch и ошибка будет обрабатываться в одном месте?

Далее в статье Ошибки — это значения предлагается решение:

func (ew *errWriter) write(buf []byte) {     if ew.err != nil {         return     }     _, ew.err = ew.w.Write(buf) }  w := &errWriter{w: fd} ew.write(p0[a:b]) ew.write(p1[c:d]) ew.write(p2[e:f]) // and so on if ew.err != nil {     return ew.err }  

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

И тут, у меня возникло ощущение, что что-то подобное в истории было. И было это в Visual Basic и связано это было с оператором If. Буду говорить о Visual Basic в прошедшем времени, так как давно не работал с ним.

В Visual Basic присутствовал оператор If и в отличии от C++ и Java отсутствовал оператор ?:. Таким образом, записывался такой код:

Dim a As Integer If CheckState() Then 	a = 12 Else 	a = 13 End If 

Со стороны VB программистов были жалобы: много повторяющегося кода, хорошо бы его сократить до одной строки и иметь возможность использовать inline. Так, возможно, появился IIf:

a = IIf(CheckState(), 12, 13) 

Всё бы хорошо, но у народа начало нарастать другое возмущение, так как здесь оказался подвох. Дело в том, что помимо кода, когда в IIf используются значения, существуют код, когда в место значений указываются функции и ожидается, что будет вызвана одна из функций в зависимости от условия, как в операторе If:

a = IIf(CheckState(), GetTrueAValue(), GetFalseAValue()) 

И для многих программистов было неожиданностью то, что в данном коде будут вызваны обе функции: GetTrueAValue и GetFalseAValue. Хотя они ожидали, что IIf будет работать, как оператор If (или как ?: в С++), т.е. они не понимали, какой смысл вызывать вторую функцию, когда её результирующее значение не имеет смысла.

Дело оказалось в том, что некоторые программисты по началу воспринимали IIf как оператор If, но на деле IIf оказался не операторам, а функцией. А чтобы вызвать функцию требуется вычислить все её аргумент, т.е. потребуется вызвать все указанные функции в аргументах. Причем, IIf был так удобно подпихнут, что не все программисты улавливали сразу, что это не оператор, а обычная функция.

Суть оператор If в Visual Basic (да и в других языках) определять участки кода, которые должны выполняться в зависимости от условия. Попытка использовать IIf дало лишь поверхностное решение и не оправдало основного ожидания от неё – вызывать ту или иную функцию в зависимости от условия.

Так вот. Вернёмся к Go и к практике, которую предлагают для работы с ошибками. У меня, сложилось такое впечатление, что выше приведённая практика обработки ошибок очень напоминает IIf в Visual Basic. Расширим пример:

w := &errWriter{w: fd} ew.write(getAB()) ew.write(getCD()) ew.write(getEF()) // and so on if ew.err != nil {     return ew.err } 

Соответственно, здесь возникает вопрос: если в строке ew.write(getAB()) возникает ошибка, то почему выполняются getCD() и getEF(), когда в их результирующих значениях смысла нет?

Почему возникает такой вопрос? Дело в том, что ошибка — это значение, которое определяет, какой код должен выполняться. И это четко видно в первоначальном способе обработки ошибок в Go.

_, err = fd.Write(getAB()) if err != nil {     return err } _, err = fd.Write(getCD()) if err != nil {     return err } _, err = fd.Write(getEF()) if err != nil {     return err } 

Значение ошибки обрабатывается оператором if. И обработка значения ошибки в if как раз и определяет, какой код должен быть выполнен дальше. При чём в большинстве случаев для принятия решения, важно наличие ошибки, а не её содержания.

По моему мнению, ty-catch-finally как раз и занимаются тем, чем нужно, определяют код, который будет выполняться в зависимости от возникшего исключения.

P.S.: Тут вспомнилось из VB:

On Error Resume Next 

Но лучше бы не вспоминалось. Чур меня, чур.

ссылка на оригинал статьи http://habrahabr.ru/post/271167/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *