How do I learn Go for JavaScript

Stop and go!

The new language should lead to direct monetary advantages as quickly as possible. This approach ensured that Go was geared towards what was known and what was already there. The product is a work tool trimmed for maximum efficiency, the development of which focuses on the needs of productive developers.

A special feature is the lifetime compatibility guarantee specified on the website. The development team is committed to maintaining downward compatibility for the entire lifespan of Go 1.x: unlike Ruby, there will be no language changes for the foreseeable future that affect “well-behaved” code.

Go here

Google offers precompiled binaries for Windows, Mac OS, and Linux. Due to the focus on concurrency, it is advisable to take your first steps on a multi-core machine. The following program examples were created on a quad-core AMD64 workstation using Ubuntu 14.04 as the operating system.

The installation of the program files is not particularly complex: the archive is extracted to the location suggested by Google, the declaration of the path variable listed in the precompiled binary files ensures that script and the like can find the tools you need without any problems. By the way, Go also works on single-board computers with an ARM processor: Unfortunately, there is no binary distribution here, which makes manual compilation necessary.

Unfortunately, providing the compiler infrastructure is only half the battle. Go requires a comparatively complex folder structure. If your code does not conform to this format, compiler errors will occur. For the following examples, create a directory tree that looks like the in illustration 1 is constructed.

Fig. 1: A Go Workspace consists of three folders

The actual source code of your solution is - of course - in the directory / src. Every projectpackage lies in its own folder structure, which can also consist of a Git or Mercurial repository if desired. Of go install compiled binaries end up in / am /, while / pkg / is responsible for the inclusion of libraries and the like.

Go assumes the presence of the GOPATHEnvironment variables that point to the workspace directory to be used. Since you sometimes work with several workspaces, it makes sense to declare the variable according to the following scheme when creating the terminal:

export GOPATH = / home / farhan / desk / TamsHaus / GoWorkspace

For convenience, some programmers add the / amDirectory to the path. This optional step makes running the compilations easier, but it is not absolutely necessary.

Go says hello

For decades, the output of "Hello World" on the screen has been the standard introductory example. Our variant turns out to be a little more complicated because it is supposed to introduce some special features of the programming language. In a subfolder of / src / a file called SUSSample1.go and add the content from Listing 1 to it.

> Listing 1 package main import “fmt” func doSomething (howManyTimes int) {for i: = 0; i

Go is delivered with a lexical scanner (also called tokenizer or lexer), which automatically generates the semicolons to be entered manually in C and Java. The manual entry of a semicolon is only necessary if you want to specify the end of a command "explicitly". An example of this can be found in the in doSomething implemented for loop.

This behavior affects the placement of curly brackets. In both C and Java, the following notation of a selection is legitimate - apart from the lack of brackets that are not provided in Go:

if i As part of the compilation, the lexer would insert a semicolon after the comparison between i and f, which leads to a compiler error. The notation shown in the code example with the semicolon at the "end" of the parameter is therefore mandatory - not only for functions.

Declarations and more

Guido Krüger's legendary textbook on C (Krüger, Guido: “Go to C-Programming”, Addison-Wesley, 2007) contained a page with complex declarations, the parsing of which poses serious difficulties even for experienced developers. Go solves this problem by borrowing from Pascal: The variable name always comes before the declaration of the type.

In order to save unnecessary input processes, the keywords provided in Pascal including the colon have been deleted without replacement, which leads to the syntax demonstrated in the snippet:

x int p * int a [3] int

Pascal is with the := Operator that is responsible for assignments. In Go, it enables the declaration of a variable, the type of which depends on the parameter used as the initialization value. We use this in the for-Loop to create the run variable (for i: = 0;). Go remains a strictly typed language. The lexer recognizes the type of constant used for initialization. Google only saves the programmer from having to explicitly enter the type.

In practice, the optimizations lead to a not inconsiderable increase in readability: The circular interpretation of more complex declarations required for C is no longer necessary.

Compile me

More complex programs can be created by executing go install compile. The command copies the result of its work into the / amFolder of the workspace. Since we only need to create a single file, this is overkill. Fortunately, the Go compiler also offers build a simpler variant that stores the binary file in the current directory:

farhan @ FARHAN14: ~ / Desk / TamsHaus / GoWorkspace / src $ go build tamoggemon.com/susexample1/SUSExample1.go

Go differs from other programming languages ​​in that the specification contains strict instructions about how to format it. Google delivers gofmt a ready-made tool that transforms any code into the canonical representation and outputs the result in the command line (Fig. 2).

Fig. 2: “gofmt” nips Flame Wars on the subject of formatting in the bud

Multi-valued functions

Methods are an elementary means of breaking down complex programs. The introduction of subs and funcs was a quantum leap. The basic concept has not been touched since the Pleistocene of computer science. A function is and remains a command that delivers a return value. Structs and arrays allow multiple values ​​to be returned, but require additional code.

Go supports functions with multiple return parameters. These are ideal for reporting errors or returning status information. As an example, let's look at a function taken from the documentation that fetches data from a reader (Listing 2).

Listing 2 func ReadFull (r Reader, buf [] byte) (n int, err error) {for len (buf)> 0 && err == nil {var no int no, err = r.Read (buf) n + = no buf = buf [no:]} return} n, e = ReadFull (anR, aBuf)

The key word func can receive two parameter lists to be put in brackets. The first declares the values ​​to be introduced into the function, while the second list specifies the variables to be returned to the caller.

In practical go-code, you keep stumbling across an optimization. It is always useful if the values ​​to be returned are always "en bloque" - in this case there is no need to name the output parameters without replacement:

func nextInt (b [] byte, i int) (int, int) {. . . return x, i}

Slice over

Arrays are "first class" in Go data types. Passing an array to a function provides the method body with a copy of the stack. This behavior is sub-ideal in several respects: Copy operations create additional overhead; any intended communication between two methods is made more difficult.

The address operator allows a pointer to a typed array to be passed. This approach, known from C, is not particularly popular in Go. The idiomatically correct approach is to enable the method to process a slice.

The easiest way to understand the role of a slice is to look at its implementation. The data type consists of a header object that contains a pointer to the data memory to be used and two index variables to describe the area of ​​interest. The following structure declaration can be found on the website, which is only of academic importance - it is not possible to address the header of a slice directly:

type SliceHeader struct {Data uintptr Len int Cap int}

In the context of the declaration, slices differ from normal arrays only in that the square brackets remain empty. Let's look at this with a small example that overwrites the contents of an array with other elements:

func processString (workOnMe [] int) {for i: = 0; i workOnMe is in processString Declared without size information. This is sufficient to mark the function as a slice processor for the interpreter: Arrays are only created in Go if the developer specifies a combination of data type and size.

Main creates a new array in the next step println is output in the command line. The square bracket after the name of the array tells Go to create a slice that spans the entire contents of the field. The second call to println the result can then be checked:

func main () {b: = [5] int {1, 2, 3, 4, 5} fmt.Println (b) processString (b [:]) fmt.Println (b)}

The desired behavior of the demo program can be proven by execution. The contents of the array were taken from processString completely replaced:

farhan @ FARHAN14: ~ / Desk / TamsHaus / GoWorkspace / src $ ./SUSDemo [1 2 3 4 5] [22 22 22 22 22]

It is not absolutely necessary to pass entire arrays to slice-processing functions. The slice operator allows you to specify start and end points; the method built into Go make creates new slices and the associated storage array.

Let's look at this with a small change to the example in Listing 3, which printf supplied with different slice types.

Listing 3 func main () {b: = [5] int {1, 2, 3, 4, 5} fmt.Println (b [:]) fmt.Println (b [2:]) fmt.Println (b [ : 3]) fmt.Println (b [2: 4])}

As in the previous example, the behavior of the program can only be meaningfully understood using its command line output:

farhan @ FARHAN14: ~ / Desk / TamsHaus / GoWorkspace / src $ ./SUSExample1 [1 2 3 4 5] [3 4 5] [1 2 3] [3 4]

At this point, a reference to the Garbage Collector integrated in Go is allowed. The programming language automatically removes memory that is no longer required - similar to Java - from time to time. It is not possible with home remedies to release an item for immediate destruction.

What is a string?

C represents strings as charArray. This numerically simple procedure simplifies the processing of the data it contains, but can cause unpleasant side effects in conjunction with constants. Go circumvents this problem by stating that a string is - per se - immutable.

This has interesting practical implications insofar as a slice created from a string can be addressed normally. To clarify this question, we use a small test program that converts a string containing a constant into a slice and then exchanges a few characters (Listing 4).

Listing 4 func main () {s: = "Teststring" fmt.Println (s) c: = [] byte (s) c [2] = 'B' fmt.Println (string (c)) fmt.Println (s)}

If you run the program in the command line, you get the following output:

farhan @ FARHAN14: ~ / Desk / TamsHaus / GoWorkspace / src $ ./SUSDemo test string TeBtstring test string

Funnily enough, this program only works if you convert the string to a slice. With the direct access shown in the snippet, an error of the type "cannot assign“:

func main () {s: = "Test" fmt.Println (s) s [0] = 'h' fmt.Println (s)}

When creating multilingual programs, the Unicode capability of the language must be taken into account. Our slice shown above will return the individual Unicode bytes in the case of more "exotic" characters - you can find more information on this in the Go Blog.

Traps and Leaves

Symbian programmers hated their operating system because of an annoying peculiarity: the failure of some methods led to the triggering of a leaf. This exception-like mischief had the uncomfortable quality of "step by step" breaking up the stack by executing the program "backwards". Its destructive effect could only be stopped by a trap call.

Go implemented with panic / recover a similar system. Let's look at it with a small example that illustrates its use:

func die () {defer fmt.Println ("deferred from die") defer fmt.Println ("deferred from die for second") fmt.Println ("die sends my regards") panic ("I die")}

die () illustrates the use of two new constructs. First, that allows defer-Command the definition of actions that are only used after the method body has been processed. The commands entered there are also executed if the method is called by calling return or is eliminated by an emerging panic.

The panicMethod is used to trigger such a panic state. It accepts a parameter that can be used to further describe the error. Panics terminate the execution of all currently active routines, but carry them through defer enrolled methods ready.

Main this time starts with the definition of an error handler, which runs through defer is pushed into the stack. The subsequent call to die () ensures panic is triggered by the in func located recover- statement is intercepted:

func main () {defer func () {if err: = recover (); err! = nil {fmt.Println ("error", err)}} () die ()}

Go differs from classic TRAP / LEAVE systems insofar as that by calling recover Trap to be created in Go programs only happens after the error has been triggered; recover also delivers the panic returned error code, which can be used for further processing of the program status.

To understand the function of the overall program, it is useful to take a screenshot of the in Figure 3 to have at hand. Note that defer processes several commands according to the FIFO principle.

Fig. 3: Panic, Resume and Defer produce strange results

When implementing concurrent programs, always make sure that the stack is only unwinding in the panic thread. If its stack is used up before arecover ()Point is reached, the entire process is terminated by the runtime.

Selections in smart

Beginners are often annoyed by the comparatively “poor” functionality of Switch: C ++ limits developers to processing integers. Go uses this frequent point of criticism for a complete redesign of the selections and iterations: C and Java programmers have to do without some beloved constructs.

As a small compensation for this, there is a significantly improved switch structure that can now even process full-fledged expressions (Listing 4).

Listing 4 func unhex (c byte) byte {switch {case '0' <= c && c <= '9': return c - '0' case 'a' <= c && c <= 'f': return c - ' a '+ 10 case' A '<= c && c <=' F ': return c -' A '+ 10} return 0}

unhex () was taken from the documentation: it is considered common and idiomatic, complex if-else-To map trees via a switch statement. The missing of break-Statements is not an error, by the way. By default, every new case tag is interpreted as an implicit break.

Unnecessary code duplication can be avoided by stringing together several conditions with commas:

func shouldEscape (c byte) bool {switch c {case '', '?', '&', '=', '#', '+', '%': return true} return false}

If you absolutely need a fall-through in the style of C or Java, you have to do this by explicitly specifying the command fallthrough instruct. Another important difference concerns the structure of the loops: The do- or. while-Loops are replaced by variants of for:

// Like a C for for init; condition; post {} // Like a C while for condition {} // Like a C for (;;) for {}

Go makes it easier to assemble the conditions by changing the operator precedence. Those familiar with C ++ will surely notice one or the other difference when looking at the priority table shown in the snippet:

Go operator precedence: 1. * /% << >> & & ^ 2. + - | ^ 3. ==! = <<=>> = 4. && 5. ||

Increasing the priority of shift operators is a concession to all those who use Go to implement hardware-related code (keyword x << 8 + y << 16). Also note that the unary operators ++ and - can only be used in statements.

It works without objects

The triumphant advance of object-oriented programming has ensured that most computer scientists today work with has-one and is-one relationships. Go requires radical rethinking at this point: Concepts such as classes and inheritance are not known to the language. Instead, Google works with structures extended in the C # style, which can also implement one or more interfaces.

We want to look at this using an example interface. Rocket specifies the basic methods necessary for the realization of a torpedo; the function fire () is responsible for triggering a rocket launch:

type rocketInterface interface {Fire () getName () string} func fireRocket (rocket rocketInterface) {rocket.Fire ()}

The actual realization of a rocket then takes place according to the following scheme:

type bisnovat struct {myName string} func (me bisnovat) Fire () {fmt.Println ("Bisnovat Fired")} func (me bisnovat) getName () string {return me.myName}

Go has no "implements" operator. A type is considered compatible if it contains all functions declared in the interface: It is theoretically possible to build an interface on a foreign type.

Method declarations in Go only differ from normal functions in that they have an additional element called a receiver. It is a pointer that points to the "object instance" responsible for calling the function. The method body can use it at runtime to manipulate the calling element. As part of the compilation, the receiver is used to declare the type that must be endowed with the method.

The code responsible for creating the objects is interesting in that it demonstrates two different initialization methods:

func main () {r: = bisnovat {} fireRocket (r) pR: = new (bisnovat) fireRocket (* pR)}

Curved brackets initiate the construction of a "normal" Struct instance in Go:

The brackets can enclose a list of values ​​that go into the newly generated instance. The newOperator also creates a new instance, but returns a pointer instead. This can be "deaddressed" with the * operator, but - by definition - does not allow any further pointer arithmetic.

Interface implementations can be nested and even clamped to extend built-in types. For more information on these options, see the Interfaces section on Github.

Threading, goodbye

Experienced IT trainers secretly admit that the average programmer is overwhelmed with the creation of sophisticated concurrent products. In addition to misunderstandings caused by the training, this is also due to the fact that working with Mutex and Co. is not necessarily one of the simpler areas of computer science.

Go bypasses the problem area by introducing a new and comparatively radical programming paradigm. As goroutine The so-called “threads” differ from concurrency constructs implemented in other languages ​​in that they are not always allocated 1: 1 to operating system threads. It can even be the case that several threads work together on a goroutine.

More seriously, memory sharing is undesirable in Go programs. Communication between each goroutines then takes place via so-called channels, which function like a kind of stream.

As an example, we are implementing a program that outputs the information supplied by two goroutines working in an endless loop (Listing 5).

Listing 5 func data source (i int, ch chan int) {for {ch <- i}} func main () {chanA: = make (chan int) chanB: = make (chan int) go data source (1, chanA) go data source ( 2, chanB) for {fmt.Println (<- chanA) fmt.Println (<- chanB)}}

goroutines are basically normal functions that are called by the command go is triggered. New channels are created by calling make (). The unbuffered variant used here has the unpleasant property of pausing program execution until both read and write access takes place. The resulting deadlock ensures that the two routines always trigger "one after the other".

Mutexes can be found in the standard language library, which cannot be discussed here for reasons of space. With the Select command, Go also offers syntactic sugar that helps with communication. Further information can be found in the Go Resources.

No bad includes!

C / C ++ programs have the uncomfortable property of "growing" during compilation. A ten megabyte project can - after all includes have been resolved - have pushed ten gigabytes of data towards the preprocessor. It follows from the logic that a large part of it consists of header files loaded multiple times.

As part of the development of Go, special attention was paid to managing includes as efficiently as possible. The most important difference to C ++ is that including an element that is not required leads to a compiler error: The dependency tree of a Go project must always remain "clean".

Learn more

There is hardly a programming language that can be fully described in an article in a specialist magazine. While the omissions in academic languages ​​are bearable, the length restriction was particularly difficult for me this time. Go contains too many features to deserve attention and save time in practice.

Developers who are experienced with C ++ should first read the official instructions for those who want to switch. The comparatively compact document shows a large number of interesting differences, but uses what is already known. Effective Go stands out from the accessible documentation. Here, too, developers willing to switch get a brief tour of important aspects of the language. Specific problems are explained in Go by Example.

Friends of complete textbooks can work through the online go-book. The work deals with the standard library, which is not discussed here for reasons of space.

More advanced topics are discussed in various textbooks: Thanks to the support of Google, there is now a wide range of English-language literature.

Conclusion

Go frustrates those switching because of its similarity to C and Java. Anyone who deals with Ruby or Lisp assumes from the outset that they will only be able to use little existing knowledge. The syntax of Go looks familiar at first glance - the compiler errors that occur during the conversion and the associated loss of “familiar” things are then doubly painful.

After overcoming these transition difficulties, Go has proven to be an efficient tool for increasing productivity. The much faster compilation of large code bases is only half the battle. If you have to enter fewer control characters, you save time.

Developer magazine

This article was published in the developer magazine.

Of course, you can also read the developer magazine digitally via the developer.kiosk in your browser or on your Android and iOS devices. The Developer Magazine is also available in our shop by subscription or as a single issue.

Lead image: Traditional Chinese Board Game - Go via Shutterstock.com / Copyright: grasycho