- PVSM.RU - https://www.pvsm.ru -

man!( Go => D ).basics

Если вы уже наигрались с Go, устали от копипасты, ручного жонглирования типами и подумываете вернуться на какой-нибудь Python или, прости господи, PHP, то позвольте предложить вам попробовать язык D, где типизация хоть и тоже статическая, но она не путается под ногами и позволяет писать не менее выразительный код, чем на языках с динамической типизацией. А чтобы переход был не такой болезненный, вашему вниманию предлагается перевод Tour of the Go [1] c эквивалентным кодом на D [2] и краткими пояснениями.

Часть первая. Основы.

Hello World

Go [3]

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

D [4]

module main;

import std.stdio;

void main()
{
    // stdout.writeln( "Hello, 世界" );
    writeln( "Hello, 世界" );
}

Разница не значительная, разве что в D пространство имён можно не указывать, если нет конфликта имён импортированных из разных модулей. Также стоит обратить внимание на обязательные точки с запятыми в конце строк — в D они, к сожалению, обязательны.

Packages

Go [5]

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println("My favorite number is", rand.Intn(10))
}

D [6]

module main;

import std.stdio;
import std.random;

void main()
{
    writeln( "My favorite number is ", uniform( 0 , 10 ) );
}

Тут тоже всё одинаково, разве что в Go при импорте указывается путь к модулю, а в D используется имя модуля, задаваемое директивой "module", или автоматически выводимое из пути к файлу, если эта директива не указана.

Imports

В Go рекомендуется группировать импорты в одну директиву. [7]

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Printf("Now you have %g problems.", math.Sqrt(7))
}

В D тоже так можно, но особенности синтаксиса не располагают к этому: [8]

module main;

import
    std.stdio,
    std.math;

void main()
{
    writefln( "Now you have %f problems.", 7f.sqrt );
}

Кроме того, в D импорты можно указывать в любом блоке, а не только в начале файла: [9]

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 модуль экспортирует лишь то, что начинается с большой буквы: [10]

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. [11]

Functions

Go [12]

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 [13]

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 обобщённое программирование, позволяющее реализовать функцию сразу для любых подходящих типов [14]:

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 для любой функции можно указать в дополнительных круглых скобках параметры времени компиляции [15], куда можно либо явно передать типы, либо они могут быть выведены автоматически компилятором из типов аргументов.

Multiple results

Go [16]

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 нет возможности возвратить из функции несколько отдельных значений, но можно вернуть кортеж: [17]

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
}

А при необходимости можно и распаковать возвращаемый кортеж в уже существующие переменные: [18]

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 [19]

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 ничего такого не поддерживает, так что возвращаем либо структуру, либо опять же кортеж, но с именованными элементами: [20]

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. [21]

Variables

Go [22]

package main

import "fmt"

var c, python, java bool

func main() {
    var i int
    fmt.Println(i, c, python, java)
}

D [23]

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 [24]

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 [25]

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. [26]

Go [27]

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 [28]

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 константы времени компиляции [29] создаются через ключевое слово "enum" — их значение инлайнится в место их использования. А вот ключевое слово "const" имеет несколько иное значение — это модификатор доступа [30], запрещающий нам изменять значение переменной (но в другом месте программы у нас может быть доступ на редактирование).

Zero values

Go [31]

package main

import "fmt"

func main() {
    var i int
    var f float64
    var b bool
    var s string
    fmt.Printf("%v %v %v %qn", i, f, b, s) // 0 0 false ""
}

D [32]

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" [33], хранящее значение по умолчанию для этого типа.

Type conversions

Go требует ручного перевода значения из одного типа в другой: [34]

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 достаточно умён, чтобы требовать ручного перевода типов лишь когда это может привести к потере данных: [35]

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 позволяют задавать числа, которые невозможно использовать в рантайме без потерь: [36]

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 при компиляции используются те же типы, что и при выполнении, так что и значения констант имеют те же ограничения: [37]

module main;

import std.stdio;

enum Big = 1L << 100; // Error: shift by 100 is outside the range 0..63
enum Small = Big >> 99;

Автор: vintage

Источник [38]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/115511

Ссылки в тексте:

[1] Tour of the Go: https://tour.golang.org/list

[2] D: http://dlang.org/

[3] Go: https://tour.golang.org/welcome/1

[4] D: http://dpaste.dzfl.pl/2b25df9b2ccc

[5] Go: https://tour.golang.org/basics/1

[6] D: http://dpaste.dzfl.pl/98c11426ff17

[7] В Go рекомендуется группировать импорты в одну директиву.: https://tour.golang.org/basics/2

[8] В D тоже так можно, но особенности синтаксиса не располагают к этому:: http://dpaste.dzfl.pl/638d82048cd2

[9] Кроме того, в D импорты можно указывать в любом блоке, а не только в начале файла:: http://dpaste.dzfl.pl/ab74342644e4

[10] В Go модуль экспортирует лишь то, что начинается с большой буквы:: https://tour.golang.org/basics/3

[11] Подробнее о модульной системе D.: https://dlang.org/spec/module.html

[12] Go: https://tour.golang.org/basics/4

[13] D: http://dpaste.dzfl.pl/fac4f221e7e6

[14] отсутствующее в Go обобщённое программирование, позволяющее реализовать функцию сразу для любых подходящих типов: http://dpaste.dzfl.pl/e471b3d5f7b3

[15] В D для любой функции можно указать в дополнительных круглых скобках параметры времени компиляции: https://dlang.org/spec/template.html#function-templates

[16] Go: https://tour.golang.org/basics/6

[17] В D нет возможности возвратить из функции несколько отдельных значений, но можно вернуть кортеж:: http://dpaste.dzfl.pl/66e8b4f1fa8a

[18] А при необходимости можно и распаковать возвращаемый кортеж в уже существующие переменные:: http://dpaste.dzfl.pl/dc1d134371da

[19] Go: https://tour.golang.org/basics/7

[20] Достаточно сомнительный синтаксический сахар. D ничего такого не поддерживает, так что возвращаем либо структуру, либо опять же кортеж, но с именованными элементами:: http://dpaste.dzfl.pl/ff38179ae419

[21] Подробнее о кортежах в D.: https://dlang.org/phobos/std_typecons.html#.Tuple

[22] Go: https://tour.golang.org/basics/8

[23] D: http://dpaste.dzfl.pl/ac108f1ae426

[24] Go: https://tour.golang.org/basics/10

[25] D: http://dpaste.dzfl.pl/ce3007e3f95f

[26] Подробнее о типах в D.: https://dlang.org/spec/type.html

[27] Go: https://tour.golang.org/basics/11

[28] D: http://dpaste.dzfl.pl/b9ba564a605a

[29] константы времени компиляции: https://dlang.org/spec/enum.html

[30] модификатор доступа: https://dlang.org/spec/const3.html

[31] Go: https://tour.golang.org/basics/12

[32] D: http://dpaste.dzfl.pl/49d43ab743fe

[33] специальное поле "init": https://dlang.org/spec/property.html#init

[34] Go требует ручного перевода значения из одного типа в другой:: https://tour.golang.org/basics/13

[35] D достаточно умён, чтобы требовать ручного перевода типов лишь когда это может привести к потере данных:: http://dpaste.dzfl.pl/d499e66d3895

[36] Численные константы в Go позволяют задавать числа, которые невозможно использовать в рантайме без потерь:: https://tour.golang.org/basics/16

[37] В D при компиляции используются те же типы, что и при выполнении, так что и значения констант имеют те же ограничения:: http://dpaste.dzfl.pl/e6bec95a3438

[38] Источник: https://habrahabr.ru/post/279657/