Если вы уже наигрались с Go, устали от копипасты, ручного жонглирования типами и подумываете вернуться на какой-нибудь Python или, прости господи, PHP, то позвольте предложить вам попробовать язык D, где типизация хоть и тоже статическая, но она не путается под ногами и позволяет писать не менее выразительный код, чем на языках с динамической типизацией. А чтобы переход был не такой болезненный, вашему вниманию предлагается перевод Tour of the Go c эквивалентным кодом на D и краткими пояснениями.
Go
D
Разница не значительная, разве что в D пространство имён можно не указывать, если нет конфликта имён импортированных из разных модулей. Также стоит обратить внимание на обязательные точки с запятыми в конце строк — в D они, к сожалению, обязательны.
Go
D
Тут тоже всё одинаково, разве что в Go при импорте указывается путь к модулю, а в D используется имя модуля, задаваемое директивой "module", или автоматически выводимое из пути к файлу, если эта директива не указана.
В Go рекомендуется группировать импорты в одну директиву.
В D тоже так можно, но особенности синтаксиса не располагают к этому:
Кроме того, в D импорты можно указывать в любом блоке, а не только в начале файла:
В Go модуль экспортирует лишь то, что начинается с большой буквы:
В D же экспортируется лишь то, что объявлено в public секции модуля (которая по умолчанию), либо помечено модификатором доступа public:
Подробнее о модульной системе D.
Go
D
В Go тип обычно следует в конце, а в D — более традиционно — в начале. Кроме того, любую функцию в D можно вызвать как метод, что позволяет элегантно расширять сторонние типы. Go же позволяет не повторять одинаковые типы идущих друг за другом параметров. Тут же стоит упомянуть отсутствующее в Go обобщённое программирование, позволяющее реализовать функцию сразу для любых подходящих типов:
В D для любой функции можно указать в дополнительных круглых скобках параметры времени компиляции, куда можно либо явно передать типы, либо они могут быть выведены автоматически компилятором из типов аргументов.
Go
В D нет возможности возвратить из функции несколько отдельных значений, но можно вернуть кортеж:
А при необходимости можно и распаковать возвращаемый кортеж в уже существующие переменные:
Go
Достаточно сомнительный синтаксический сахар. D ничего такого не поддерживает, так что возвращаем либо структуру, либо опять же кортеж, но с именованными элемен��ами:
Оператор [] возвращает так называемый "срез", то есть массив элементов.
Подробнее о кортежах в D.
Go
D
В целом, объявления переменных очень похожи, разве что синтаксис Go несколько более многословен.
Go
D
Оба языка умеют выводить тип переменной из инициализирующего выражения. Однако подход Go с разделением объявления переменных на список имён и список значений довольно не нагляден и провоцирует ошибки.
Таблица соответствия типов:
Существенное различие в том, что размер int и uint в Go зависит от платформы, а в D — не зависит. Также D контролирует, чтобы мнимые числа не перепутались с реальными. Кроме того, D позволяет работать с вещественными числами большего размера (80 бит), а с символами — меньшего (8 и 16 бит). Подробнее о типах в D.
Go
D
В D у каждого типа есть свойства, позволяющие получить основные связанные с типом константы. Стоит обратить внимание, что в D константы времени компиляции создаются через ключевое слово "enum" — их значение инлайнится в место их использования. А вот ключевое слово "const" имеет несколько иное значение — это модификатор доступа, запрещающий нам изменять значение переменной (но в другом месте программы у нас может быть доступ на редактирование).
Go
D
В D у каждого типа есть специальное поле "init", хранящее значение по умолчанию для этого типа.
Go требует ручного перевода значения из одного типа в другой:
D достаточно умён, чтобы требовать ручного перевода типов лишь когда это может привести к потере данных:
Численные константы в Go позволяют задавать числа, которые невозможно использовать в рантайме без потерь:
В D при компиляции используются те же типы, что и при выполнении, так что и значения констант имеют те же ограничения:
Часть первая. Основы.
Hello World
Go
package main import "fmt" func main() { fmt.Println("Hello, 世界") }
D
module main; import std.stdio; void main() { // stdout.writeln( "Hello, 世界" ); writeln( "Hello, 世界" ); }
Разница не значительная, разве что в D пространство имён можно не указывать, если нет конфликта имён импортированных из разных модулей. Также стоит обратить внимание на обязательные точки с запятыми в конце строк — в D они, к сожалению, обязательны.
Packages
Go
package main import ( "fmt" "math/rand" ) func main() { fmt.Println("My favorite number is", rand.Intn(10)) }
D
module main; import std.stdio; import std.random; void main() { writeln( "My favorite number is ", uniform( 0 , 10 ) ); }
Тут тоже всё одинаково, разве что в Go при импорте указывается путь к модулю, а в D используется имя модуля, задаваемое директивой "module", или автоматически выводимое из пути к файлу, если эта директива не указана.
Imports
В Go рекомендуется группировать импорты в одну директиву.
package main import ( "fmt" "math" ) func main() { fmt.Printf("Now you have %g problems.", math.Sqrt(7)) }
В D тоже так можно, но особенности синтаксиса не располагают к этому:
module main; import std.stdio, std.math; void main() { writefln( "Now you have %f problems.", 7f.sqrt ); }
Кроме того, в D импорты можно указывать в любом блоке, а не только в начале файла:
module main; void main() { import std.stdio; { import std.math; writefln( "Now you have %f problems.", 7f.sqrt ); } writefln( "Now you have %f problems.", 7f.sqrt ); // Error: no property 'sqrt' for type 'float' }
Exported names
В Go модуль экспортирует лишь то, что начинается с большой буквы:
package main import ( "fmt" "math" ) func main() { fmt.Println(math.pi) // cannot refer to unexported name math.pi }
В D же экспортируется лишь то, что объявлено в public секции модуля (которая по умолчанию), либо помечено модификатором доступа public:
module math; import std.math; auto PI = std.math.PI; private: public auto pip = std.math.PI; auto pi = std.math.PI;
module main; import std.stdio; import math; void main() { writeln( PI ); writeln( pi ); // Error: module main variable math.pi is private writeln( pip ); }
Подробнее о модульной системе D.
Functions
Go
package main import "fmt" // func add(x int, y int) int { func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) }
D
module main; import std.stdio; int add( int x , int y ) { return x + y; } void main() { // writeln( add( 42 , 13 ) ); writeln( 42.add( 13 ) ); }
В Go тип обычно следует в конце, а в D — более традиционно — в начале. Кроме того, любую функцию в D можно вызвать как метод, что позволяет элегантно расширять сторонние типы. Go же позволяет не повторять одинаковые типы идущих друг за другом параметров. Тут же стоит упомянуть отсутствующее в Go обобщённое программирование, позволяющее реализовать функцию сразу для любых подходящих типов:
module main; import std.stdio; auto add( X , Y )( X x , Y y ) { return x + y; // Error: incompatible types for ((x) + (y)): 'int' and 'string' } void main() { // writeln( 42.add!( int , float )( 13.3 ) ); writeln( 42.add( 13.3 ) ); // 55.3 writeln( 42.add( "WTF?" ) ); // Error: template instance main.add!(int, string) error instantiating }
В D для любой функции можно указать в дополнительных круглых скобках параметры времени компиляции, куда можно либо явно передать типы, либо они могут быть выведены автоматически компилятором из типов аргументов.
Multiple results
Go
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) }
В D нет возможности возвратить из функции несколько отдельных значений, но можно вернуть кортеж:
module main; import std.stdio; import std.typecons; auto swap( Item )( Item[2] arg... ) { return tuple( arg[1] , arg[0] ); } void main() { auto res = swap( "hello" , "world" ); writeln( res[0] , res[1] ); // worldhello }
А при необходимости можно и распаковать возвращаемый кортеж в уже существующие переменные:
module main; import std.stdio; import std.meta; import std.typecons; auto swap( Item )( Item[2] arg... ) { return tuple( arg[1] , arg[0] ); } void main() { string a , b; AliasSeq!( a , b ) = swap( "hello" , "world" ); writeln( a , b ); // worldhello }
Named return values
Go
package main import "fmt" func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return } func main() { fmt.Println(split(17)) }
Достаточно сомнительный синтаксический сахар. D ничего такого не поддерживает, так что возвращаем либо структуру, либо опять же кортеж, но с именованными элемен��ами:
module main; import std.stdio; import std.typecons; auto split( int sum ) { auto x = sum * 4 / 9; auto y = sum - x; return tuple!( "x" , "y" )( x , y ); } void main() { // auto res = split( 17 ); writeln( res.x , res.y ); // writeln( split( 17 )[] ); writeln( 17.split[] ); // 710 }
Оператор [] возвращает так называемый "срез", то есть массив элементов.
Подробнее о кортежах в D.
Variables
Go
package main import "fmt" var c, python, java bool func main() { var i int fmt.Println(i, c, python, java) }
D
module main; import std.stdio; // bool c , python , java; bool c; bool python; bool java; void main() { int i; writeln( i , c , python , java ); // 0falsefalsefalse }
В целом, объявления переменных очень похожи, разве что синтаксис Go несколько более многословен.
Short variable declarations
Go
package main import "fmt" func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) }
D
module main; import std.stdio; void main() { int i = 1 , j = 2; auto k = 3; auto c = true , python = false , java = "no!"; writeln( i , j , k , c , python , java ); // 123truefalseno! }
Оба языка умеют выводить тип переменной из инициализирующего выражения. Однако подход Go с разделением объявления переменных на список имён и список значений довольно не нагляден и провоцирует ошибки.
Basic types
Таблица соответствия типов:
Go D --------------------- void bool bool string string int int byte byte int8 byte int16 short int32 int int64 long uint unint uint8 ubyte uint16 ushort uint32 uint uint64 ulong uintptr size_t ptrdiff_t float32 float float64 double real ifloat idouble ireal complex64 cfloat complex128 cdouble creal char wchar rune dchar
Существенное различие в том, что размер int и uint в Go зависит от платформы, а в D — не зависит. Также D контролирует, чтобы мнимые числа не перепутались с реальными. Кроме того, D позволяет работать с вещественными числами большего размера (80 бит), а с символами — меньшего (8 и 16 бит). Подробнее о типах в D.
Go
package main import ( "fmt" "math/cmplx" ) var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) ) func main() { const f = "%T(%v)\n" fmt.Printf(f, ToBe, ToBe) fmt.Printf(f, MaxInt, MaxInt) fmt.Printf(f, z, z) }
D
module main; import std.stdio; import std.math; bool ToBe = false; ulong MaxInt = ulong.max; cdouble z = sqrt( -5 + 12i ); void main() { enum f = "%s(%s)"; writefln( f , typeid( ToBe ) , ToBe ); // bool(false) writefln ( f , typeid( MaxInt ) , MaxInt ); // ulong(18446744073709551615) writefln( f , typeid( z ) , z ); // cdouble(2+3i) }
В D у каждого типа есть свойства, позволяющие получить основные связанные с типом константы. Стоит обратить внимание, что в D константы времени компиляции создаются через ключевое слово "enum" — их значение инлайнится в место их использования. А вот ключевое слово "const" имеет несколько иное значение — это модификатор доступа, запрещающий нам изменять значение переменной (но в другом месте программы у нас может быть доступ на редактирование).
Zero values
Go
package main import "fmt" func main() { var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n", i, f, b, s) // 0 0 false "" }
D
module main; import std.stdio; void main() { writefln( "%s %s %s \"%s\"" , int.init , double.init , bool.init , string.init ); // 0 nan false "" }
В D у каждого типа есть специальное поле "init", хранящее значение по умолчанию для этого типа.
Type conversions
Go требует ручного перевода значения из одного типа в другой:
package main import ( "fmt" "math" ) func main() { var x int = 3 var y uint = 4 var f float64 = math.Sqrt(float64(uint(x*x) + y*y)) var z uint = uint(f) fmt.Println(x, y, z) // 345 }
D достаточно умён, чтобы требовать ручного перевода типов лишь когда это может привести к потере данных:
module main; import std.stdio; import std.conv; void main() { int x = 3; uint y = 4; double f = ( x^^2 + y^^2 )^^0.5; uint z = f.to!uint; writeln( x , y , z ); // 345 }
Numeric Constants
Численные константы в Go позволяют задавать числа, которые невозможно использовать в рантайме без потерь:
package main import "fmt" const ( // Create a huge number by shifting a 1 bit left 100 places. // In other words, the binary number that is 1 followed by 100 zeroes. Big = 1 << 100 // Shift it right again 99 places, so we end up with 1<<1, or 2. Small = Big >> 99 ) func needInt(x int) int { return x*10 + 1 } func needFloat(x float64) float64 { return x * 0.1 } func main() { fmt.Println(needInt(Small)) // 21 fmt.Println(needInt(Big)) // constant 1267650600228229401496703205376 overflows int fmt.Println(needFloat(Small)) // 0.2 fmt.Println(needFloat(Big)) // 1.2676506002282295e+29 }
В D при компиляции используются те же типы, что и при выполнении, так что и значения констант имеют те же ограничения:
module main; import std.stdio; enum Big = 1L << 100; // Error: shift by 100 is outside the range 0..63 enum Small = Big >> 99;
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какую часть переводить следующей?
55.74%Управление потоком исполнения68
31.15%Составные типы38
25.41%Методы31
63.93%Сопрограммы78
Проголосовали 122 пользователя. Воздержались 100 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Ну что, на какой прикладной язык переходим?
3.29%C15
11.84%C++54
13.6%D62
25.44%Go116
21.49%Rust98
5.7%Java26
9.21%C#42
9.43%Другой43
Проголосовали 456 пользователей. Воздержались 128 пользователей.
