Go (язык программирования)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 23:02, 8 июня 2016.
Go (golang)
fraimed
Парадигма многопоточный, императивный, структурированный
Спроектировано Роберт Гризмер, Роб Пайк, Кен Томпсон
Печать дисциплины строгая, статическая, с выводом типов
OS UNIX, Linux, Windows
Портал: golang.org
Go - язык программирования с открытым исходным кодом, который упрощает разработку надёжного и эффективного ПО. (golang.org)[1]

Go (или golang) -(произносится [ ɡəʊ ]) компилируемый, многопоточный, статически типизированный язык программирования поддерживающий сборщик мусора.[1] Данный язык был разработан программистами в Google. Язык был изобретён для людей кто пишет (а также читает, отлаживает и контролирует) большие системы ПО. Таким образом основной целью языка является не производить исследования в поиске новых концепций. Язык стремится улучшить рабочее окружение разработчика и его коллег по работе для решения рутинных задач.[2]

История

В сентябре 21 в 2007 году, Роберт Гризмер, Роб Пайк и Кен Томпсон начали делать первые наброски основных целей на белой доске. В течении нескольких дней, цели были собраны в план того, что нужно сделать "нечто" и в некую идею того, как это должно выглядеть в конце концов. Проектирование продолжалось в свободное время от основной работы. В январе 2008 года, Кен начал работу над компилятором с целью исследовать некоторые идеи. На выходе компилятор выдавал код на C. В середине года, язык перерастает в полноценный проект. В мае 2008 года, Ян Тейлор, независимо от других, начал разработку внешнего интерфейса GCC для Go используя черновой вариант спецификации. Рус Кокс присоединился к проекту в конце 2008 года и помог воплотить прототипы языка и его библиотек в реальность. 10 ноября 2009 года, Go становится проектом с открытым исходным кодом.

Go был рождён из-за разочарования в существующих языках и рабочего окружения для системных программистов. Программирование стало слишком сложным и многообразие языков было тому частичной виной. Один делал выбор в сторону эффективной компиляции, другой - эффективное выполнение или лёгкость в написании кода. Все три аспекта в популярных языках не были доступны одновременно. Программисты, которые склонялись к простоте нежели к надёжности и производительности делали выбор в сторону динамически типизированных языков программирования таких как Python и JavaScript отвергая C++, Java.

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

Go в большой степени отоносится к семейству C подобных языков (из-за синтаксиса). На язык так же оказало влияние семейство Pascal/Modula/Oberon (объявления и пакеты), плюс некоторые идеи из языков вдохновлённых концепцией Тони Хоара CSP таких как Newsqueak и Limbo (многопоточность-конкуренция). Несмотря на всё это наследие, Go является новым языком. В каждом аспекте язык был разработан думая о том, что делает программист и как сделать программирование, или хотя бы какой либо его аспект, более эффективным, то есть более интересным.[3]

Цель

Разработчики в Google столкнулись со следующими проблемами:[4]

  • Вычислительный масштаб чрезвычайно вырос.
  • Компьютеры стали работать быстрее, но скорость разработки не ускорилась.
  • Контроль зависимостей занимает большую часть в разработки ПО на сегодняшний день, но "заголовочные файлы" языков в стиле C мешают анализу зависимостей, а также быстрой компиляции.
  • Существует растущая тенденция против громоздких систем таких как Java и C++ таким образом толкая людей использовать динамически типизированные языки такие как Python и JavaScript.
  • Некоторые фондументальные концепты такие как сборщик мусора и параллельные вычисления не особо поддерживаются в популярных системных языках программирования.
  • Появление многоядерных компьютеров породило беспокойство и путаницу.

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

  • Можно компилировать большие программы написанные на Go за считанные секунды на одном компьютере.
  • Go позволяет легко проводить анализ зависимостей и позволяет избежать многие проблемы подключения файлов (директива include) и библиотек в стиле C.
  • Система типов в Go не имеет иерархии тем самым не приходится тратить времени на выявление связей между типами. Также, несмотря на то, что Go имеет статическую типизацию язык пытается сделать типы более простыми в отличии от рядового объектно ориентированного языка программирования.
  • В основании Go заложен сборщик мусора и выполнении программы в конкурентном режиме с поддержкой коммуникации в виде каналов.
  • Из-за своих особенностей, заложенных во время разработки языка, Go нацелен на большие системы под нагрузкой.

Принципы и дизайн языка

Go стремится уменьшить количество набираемого текста. Следуя этому принципу. Разработчики попытались уменьшить беспорядок и сложность. Тем самым нету ранних объявлений (forward declarations) и нет "заголовочных файлов"; все объявления производятся ровно один раз. Инициализация выразительная, автоматическая и лёгкая в использовании. Предоставляемый синтаксис предельно прост. Конструкции такие как foo.Foo* myFoo = new(foo.Foo) сокращены с помощью :=. И самое радикальное, в языке нету иерархии типов. Типы представляются такие, какие они есть и между ними не имеются какие либо отношения. Эти упрощения позволяют Go быть выразительным.

Другой очень важный принцип в Go это ортогональный принцип. Методы могут быть реализованы абсолютно для любого типа; структуры представляют данные в то время как интерфейсы - абстракцию. Ортогональность позволяет легче понимать что происходит, когда что либо комбинируется в сложное. [5]

Отметим некоторые моменты, которые Go не поддерживает:[6]

  • неявная конвертация чисел,
  • конструкторы и деструкторы,
  • перегрузка операторов,
  • значения по умолчанию,
  • наследование,
  • дженерики (связано с внутренней реализацией, возможно добавят в Go 2.0),
  • обработка исключительных ситуаций,
  • макросы,
  • аннотация функций,
  • локальное хранилище для потоков.

Именование

Для именования переменных Go разработчики используют "camelCaseStyle", но есть один очень важный момент

  • если имя начинается с заглавной буквы, то переменная/функция/и.т.д экспортируемая

Это означает, что можно получить доступ за пределами пакета, если пакет импортирован. К примеру

package main

// после импорта мы имеем доступ к функциям/переменным/структурам
// имена которых начинаются с заглавной буквы, обьявленные на уровне пакета
import "fmt" 

func main() {
    fmt.Println("ƒ = ∑i")
}

Тот же принцип применяется для полей структур

// поскольку начинается с заглавной буквы => экспортируемая
type Example struct {
    Field1 int      // заглавная буква => public
    field2 float64 // строчная буква => private => доступ на уровне пакета
}

Синтаксис объявления

Новички в Go (особенно C разработчики) часто сталкиваются с проблемой понимания синтаксиса объявления в Go. Сравним с синтаксисом в С. Каждый разработчик на C знает о "спиральном правиле"

int *p;    // 'p' указатель на int
int a[3]; // 'a' массив из трёх int

Данные объявления понимаются легко. Рассмотрим более сложный пример

int (*(*fp)(int (*)(int, int), int))(int, int)

Начиная с этого момента объявление читается не очевидно до тех пор, пока не освоен принцип объявления в C. Go пытается разрешить эту проблему используя стиль объявления слева направо

var p *int
var a [3]int

f func(func(int,int) int, int) func(int, int) int

Возвращение нескольких значений

Одной из особенностей в Go является возможность возвращать несколько переменных. Такая возможность позволяет решить несколько проблем, с которыми приходилось сталкиваться в C: к примеру возвращение ошибки при чтении/записи в файл.

В Go, сигнатура метода записи в файл имеет следующий вид

func (file *File) Write(b []byte) (n int, err error)

и как сказано в документации, метод возвращает количество записанных байт (n) и не nil значение (error) если n != len(b).

Массивы и Слайсы

Массив в Go служит строительным блоком для слайса.[7]. Go массивы несколько отличаются от C, а именно в Go:

  • Массивы - значения. При присваивание одного массива к другому копируются все значения.
  • Поскольку в Go передача данных в функцию происходит по значению, то передача массива в функцию приводит к копированию, а не передачи указателя на массив.
  • Размер массива является частью его типа. Типы [10]int и [20]int различны.

Свойство значения может быть полезным, но затратным. Если необходимо поведение массива как в C, то можно передавать указатель на массив.

func Sum(a *[3]float64) (sum float64) {
    for _, v := range *a {
        sum += v
    }
    return
}

array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array)

Данный подход идиоматически в Go не является верным. Для этих целей в Go имеются слайсы. Слайс - обёртка над массивом, которая, если не вдоваться в подробности реализации, хранит указатель на массив. Если присвоить одному слайсу другой, то скопируется указатель на массив.

Метод Read принимает слайс, вместо указателя на массив и количества байт необходимых считать; длина внутри слайса устанавливает верхний лимит на то, сколько байт данных необходимо считать. Сигнатура метода Read из пакета os:

func (f *File) Read(buf []byte) (n int, err error)

Метод возвращает количество считанных байт и значение error. Чтобы считать первые 32 байта из buf мы используем слайс.

n, err := f.Read(buf[0:32])

Такая практика очень часто используется и в других функциях. Для большой информации о слайсах можно прочитать статью "Arrays, slices (and strings): The mechanics of 'append" и "Go Slices: usage and internals"

Обработка ошибок

Go не поддерживает исключения как в Java, таким образом единственный вариант - явная проверка.

resp, err := http.Get(url)
if err != nil {
    return err
}

doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
    return fmt.Errorf("parsing %s as HTML: %v", url, err)
}

Для большей информации о том, почему в Go нет обработки исключительных ситуаций "Why does Go not have exceptions?" и "Errors are Values".

Интерфейсы

Многие объектно-ориентированные языки как правило поддерживают интерфейсы, но в Go интерфейсы выглядят иначе. В Go интерфейсы удовлетворительно неявные[6]. Другими словами вам не нужно явно определять какие интерфейсы должен реализовывать конкретный тип.

Если что то может делать это, значит это может быть использовано здесь.[8]

Рассмотрим интерфейс Interface в пакете sort[9]

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package.  The methods require that the
// elements of the collection be enumerated by an integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int
	// Less reports whether the element with
	// index i should sort before the element with index j.
	Less(i, j int) bool
	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

Если мы хотим использовать этот интерфейс нам достаточно сделать import "sort" и реализовать методы для нашего типа а именно: Len, Less и Swap.

type Sequence []int

// Methods required by sort.Interface.
func (s Sequence) Len() int {
    return len(s)
}
func (s Sequence) Less(i, j int) bool {
    return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

После реализации методов мы можем использовать функцию sort.Sort() в которую в качестве аргумента необходимо передать значение, тип которого имеет методы Len, Less и Swap.

Многопоточность (конкуренция)

В этой секции мы лишь рассмотрим как легко запустить код в конкурентном режиме. Для этого достаточно лишь сказать go

go list.Sort()  // запустить list.Sort конкурентно; не ждать пока выполнится

Мультипоточность и конкуренция является большой темой обсуждения в программировании. Для лучшего понимания о том, что из себя представляет конкуренция и то, как она используется в Go рекомендуется прочитать серию статей/презентаций.

Go и Go!

Фрэнсис МакКейб, разработчик языка программирования Go!, опубликовал сообщение, где он требует Google сменить название языка.[10] Но после долгих дебатов в октябре 2010 года тема была закрыта со статусом неудачно.

Примечания

Ссылки