📜 ⬆️ ⬇️

man! (Go => D) .basics

If you have already played enough with Go, are tired of copy-paste, manual juggling with types and are considering returning to some Python or, God forgive me, PHP, then let me suggest you try D, where typing is static too, but it doesn't get under your feet and allows you to write no less expressive code than in languages ​​with dynamic typing. And in order to make the transition not so painful, we offer you a translation of the Tour of the Go with the equivalent code on D and brief explanations.

Part one. The basics.


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, 世界" ); } 

The difference is not significant, except that the D namespace can be omitted if there is no conflict of names imported from different modules. You should also pay attention to the mandatory semicolons at the end of the lines - in D they are, unfortunately, mandatory.

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 ) ); } 

Here, too, everything is the same, except that in Go, when importing, the path to the module is indicated, and in D the module name is used, which is specified by the "module" directive, or automatically output from the file path if this directive is not specified.

Imports


In Go, it is recommended to group imports into one directive.

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

In D, this is also possible, but the syntax features do not contribute to this:

 module main; import std.stdio, std.math; void main() { writefln( "Now you have %f problems.", 7f.sqrt ); } 

In addition, in D, imports can be specified in any block, and not just at the beginning of the file:

 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


In Go, the module exports only what begins with a capital letter:

 package main import ( "fmt" "math" ) func main() { fmt.Println(math.pi) // cannot refer to unexported name math.pi } 

In D, however, only what is declared in the public section of the module (which is by default) is exported, or it is marked with the public access modifier:

 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 ); } 

Read more about the modular system 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 ) ); } 

In Go, the type usually follows at the end, and in D, more traditionally, at the beginning. In addition, any function in D can be called as a method, which allows you to elegantly extend third-party types. Go allows you not to repeat the same types of parameters following one another. It is also worth mentioning the generalized programming missing in Go, which allows to implement the function immediately for any suitable types :

 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 } 

In D, for any function, you can specify compile-time parameters in additional parentheses , where you can either explicitly pass types, or they can be deduced automatically by the compiler from argument types.

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) } 

In D, it is not possible to return several separate values ​​from a function, but you can return a tuple:

 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 } 

And if necessary, you can unpack the returned tuple into already existing variables:

 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)) } 

Rather doubtful syntactic sugar. D does not support anything, so we return either a structure or, again, a tuple, but with named elements:

 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 } 

The [] operator returns the so-called "slice", that is, an array of elements.

Read more about tuples in D.

Variable


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 } 

In general, variable declarations are very similar, except that the Go syntax is somewhat more verbose.

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! } 

Both languages ​​can infer the type of a variable from an initializing expression. However, the Go approach with the separation of variable declarations into a list of names and a list of values ​​is not quite clear and provokes errors.

Basic types


Type mapping table:

 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 

The significant difference is that the size of int and uint in Go depends on the platform, while in D it does not. D also controls that imaginary numbers are not messed up with real ones. In addition, D allows you to work with real numbers of a larger size (80 bits), and with symbols - smaller (8 and 16 bits). Read more about types in 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) } 

In D, each type has properties that allow you to get the basic type-related constants. It should be noted that in D compilation time constants are created through the keyword "enum" - their value is inline to the place of their use. But the key word "const" has a slightly different meaning - it is an access modifier that prohibits us from changing the value of a variable (but in another place of the program we can have access to editing).

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 "" } 

In D, each type has a special field "init" that stores the default value for this type.

Type conversions


Go requires manual translation of values ​​from one type to another:

 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 is smart enough to require manual type translation only when it can lead to data loss:

 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


Numerical constants in Go allow you to specify numbers that cannot be used in runtime without loss:

 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 } 

In D, when compiling, the same types are used as in the execution, so the values ​​of the constants have the same restrictions:

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

Source: https://habr.com/ru/post/279657/


All Articles