Edward's Tech Site

this site made with Next.js 13, see the code

FORAY: Jun 25 - Go
Get more acquainted with ArangoDB and Go
  • resources
  • infos
  • Linked-In Learning: Learning Go
    • Develop basic programs with Go
      • Go
        • created at Google in 2012
        • open source
        • new versions every 6 months
        • in C-language family
          • greater simplicity
          • greater precision in syntax
          • strongly-typed language
          • popular for solutions in cloud-based services
    • What you need to know
      • kinds of applications
        • command-prompt
        • REST APIs
        • compilers and low-level system software
      • compiled language
      • based on C but influenced by other languages
        • C++, C# and Java
      • OOP based
        • encapsulation with types and structs
        • polymorphism with interfaces
        • it doesn't support type inheritance as C# and Java
          • but it has interfaces and contracts
      • you should know
        • one other language
    • Go's essential characteristics
      • Go is a compiled language
      • unlike Java which is compiled to bytecode that can run on multiple operating systems
        • Go is compiled to a form that can only run on a single operating system
          • if you compile for Windows, it can only be run on Windows
        • since it is statically typed
          • type checking is performed during the compilation process, which helps catch type errors early
          • types can be inferred but they are always known at compilation time
          • you can develop in a way that it feels like an interpreted language, but it is compiling behind the scenes
      • compiled with a statically linked runtime
        • but no external virtual machine
          • whereas in Java, you have a JVM
      • is Go object-oriented
        • yes, sort of
        • supported OOP
          • it has custom interfaces
          • custom types can implement one or more interfaces
          • "when you add functions to a type, they are now called methods"
          • you can create your own structs in Go, which have their own member fields
            • "a struct is used to create complex data structures with multiple fields"
          • so working in go has the feel of working with classes in Java and C#
        • unsupported OOP
          • no classes and no inheritance
            • no supertypes and subtypes
          • no method or operator overloading
          • no structured exception handling
            • no try, catch, finally
            • instead, error objects are returned
          • no implicit numeric conversions
            • even if such a conversion would be lossless
        • designed as
          • next-generation language for C
          • borrows from
            • C syntax
            • Pascal
              • 1970, Niklaus Wirth
            • Modula
              • 1970s, Niklaus Wirth
              • successor to Pascal
            • Oberon
              • 1986, Niklaus Wirth
      • "one of the attractions of Go is you simply don't have to do that much typing" //quote
      • "as long as you understand the fundamentals of programming, you should find Go to be a very learnable language" //quote
    • Explore basic Go syntax
      • case-sensitive
      • variable and package names are lowercase and mixed
      • public fields have initial uppercase
        • means it's exported
        • like "public" in Java/C#
      • no semicolon at end of lines
      • sensitive to whitespace
      • code blocks have braces
      • has set of built-in functions tha tare always available without having to import anything
        • e.g. len
      • learn Go at go.dev
      • Go by Example
      • Free book: The Go Programming Language (2016)
    • Experiment with the Go Playground
      • Go Playground
      • dropdown for code examples
      • format
      • share shows URL to copy
      • security sandbox
      • it fakes the file system, changes aren't persistant
      • date/time is always 2009
    • Install Go on macOS
      • installation on Mac is simple
      • export $PATH
    • Install Go on Windows
      • MSI installer
      • c:\go (although that's not where mine is)
      • go version
    • Choose an IDE for Go programming
      • mentions Eclipse
      • Sublime - not updated very much recently
      • Goland by Jetbrains, not free
      • VSCode - his choice
    • Install and configure Visual Studio Code
      • easy to install
      • install Go Extension
      • F1, go: loc
      • I see the paths in the upper window
      • the root
    • Get the exercise files from GitHub
      • pasting in URLs from GitHub
      • there are branches in the repos
      • b = begin
      • e = end
      • explains Git
    • Create a Hello World application
      • each application should be in its own folder
      • calls main file main.go
      • doesn't have to be "main" but should be all lowercase and no spaces
      • go run . doesn't work for me
      • can build like this
      • installing Go on Ubuntu with Snap
        • sudo snap install go --classic, super easy:
        • hello world works
      • installing Go on Hetzner Debian machine
    • Explore Go's variable types
      • statically typed
      • once assigned a type, a variable cannot change types
      • explicity or inferred
      • types
        • bool
        • string
        • (integers, unsigned and signed, uint is non-negative)
          • uint8
          • uint16
          • uint32
          • uint64
          • int8
          • int16
          • int32
          • int64
          • (aliases)
            • byte
            • uint
            • int
            • uintptr
        • (floating)
          • float32
          • float64
        • (complex) - won't be using these
          • complex64
          • complex128
        • (data collections)
          • Arrays
          • Slices
          • Maps
          • Structs
        • (language organization)
          • Functions
          • Interfaces
          • Channels
        • (data management)
          • Pointers
        • (custom data types that you create)
    • Declare and initialize variables
      • 003-variables/main.go
        • package main
           
          import "fmt"
           
          const path = "usr/bin"
           
          func separator() {
          fmt.Println("---")
          }
           
          func main() {
          var message string = "greetings"
          var age = 42
          var answer int
          rank := 5.4 // same as with var, but only inside functions
           
          fmt.Printf("The message is \"%s\".\n", message)
          fmt.Printf("The message type is \"%T\".\n", message)
          separator()
          fmt.Printf("The age is %d.\n", age)
          fmt.Printf("The age type is \"%T\".\n", age)
          separator()
          fmt.Printf("The answer is %d.\n", answer)
          fmt.Printf("The answer type is \"%T\".\n", answer)
          separator()
          fmt.Printf("The rank is %f.\n", rank)
          fmt.Printf("The rank is %.2f.\n", rank)
          fmt.Printf("The rank type is \"%T\".\n", rank)
          separator()
          fmt.Printf("The constant path is \"%s\".\n", path)
          fmt.Printf("The constant path type is \"%T\".\n", path)
          }
    • Get input from the console
      • 004-input/main.go
        • package main
           
          import (
          "fmt"
          "bufio"
          "os"
          "strings"
          )
           
          func main() {
          reader := bufio.NewReader(os.Stdin)
          fmt.Print("What is your name? ")
          username, _ := reader.ReadString('\n')
          username = strings.TrimSpace(username)
          fmt.Printf("Your name is \"%s\".\n", username)
          }
      • make this a global command on Windows: getname that you can type anywhere
        • built, renamed and copied to c:\go
        • added "c:\go" to system PATH
        • runs as "getname" in MS-DOS and "/c/go/getname.exe" in Hyper
      • make this a global command on Ubunut: getname that you can type anywhere
        • built, renamed and moved to /usr/local/bin
        • now you can type it anywhere and it runs
    • Convert string inputs to other types
      • get string and convert to float
        • package main
           
          import (
          "fmt"
          "bufio"
          "os"
          "strings"
          "strconv"
          )
           
          func main() {
          reader := bufio.NewReader(os.Stdin)
          fmt.Print("What is your name? ")
          username, _ := reader.ReadString('\n')
          username = strings.TrimSpace(username)
          fmt.Printf("Your name is \"%s\".\n", username)
           
          fmt.Print("What is your age? ")
          ageString, _ := reader.ReadString('\n')
          ageFloat, err := strconv.ParseFloat(strings.TrimSpace(ageString), 64)
          if(err != nil) {
          fmt.Println(err)
          } else {
          fmt.Printf(`You age to 2 decimal places is %.2f.`, ageFloat)
          }
          }
           
    • Use math operators
      • you have to convert types to add them
      • math.Round
        • package main
           
          import (
          "fmt"
          "math"
          )
           
          func main() {
           
          age := 34
          exactYears := 3.4
           
          newAge := float64(age) + exactYears
          fmt.Printf("we added %d and %f and got %f\n", age, exactYears, newAge)
          fmt.Printf("we added %v and %v and got %v\n", age, exactYears, newAge)
          fmt.Printf("we added %v years which rounded is %v years", exactYears, math.Round(exactYears))
          }
    • Use the math package
      • add float accuracy with Round and a trick
    • Work with dates and times
      • dates
        • package main
           
          import (
          "fmt"
          "time"
          )
           
          func main() {
           
          now := time.Now()
          launchDateTime := time.Date(2009, time.November, 10, 23, 0,0,0,time.UTC)
           
          fmt.Printf("Time is %v\n", now)
          fmt.Printf("Go launched at %v\n", launchDateTime)
          fmt.Printf("Go launched at %v\n", launchDateTime.Format(time.ANSIC))
          fmt.Printf("Go launched at %v\n", launchDateTime.Format(time.RFC1123))
          }
    • Solution: Create a simple calculator app
      • date format in Go
    • How memory is allocated and managed
      • new()
        • allocases but does not initialize memory, gets errors
      • make()
        • allocates and initializes, works
      • there is garbage collection
    • Reference values with pointers
      • Go has pointers
      • var p *int is a pointer
      • when you change the value that the pointer is pointing to, you also change the value
      • as in other languages
      • change the value under a pointer and you change the value
        • package main
           
          import "fmt"
           
          func printValues(i int, p *int) {
          fmt.Printf("The age is %d\n", i)
          fmt.Printf("The pointer is %d\n", p)
          fmt.Printf("The pointer points to the value %d\n", *p)
          }
           
          func main() {
          age := 34
          pAge := &age
           
          printValues(age, pAge)
          *pAge += 1
           
          fmt.Println("--- VALUE UNDER POINTER CHANGED")
          printValues(age, pAge)
          }
           
    • Store ordered values in arrays
      • it's better to use slices than arrays to represent ordered collections of values
        • but it's important to understand arrays before you understand slices
      • if you pass an array to a function, a copy is made
      • you can't easily sort them
      • you can't easily add or remove items at run time
      • instead, you should use slices
    • Manage ordered values in slices
      • slices are resizable
        • and can be sorted
      • example of slices:
      • VSCode indicated code simplifications that were possible:
    • Store unordered values in maps
      • it's common to use strings for keys
        • and any other type for values
        • simple map
      • it's unordered and you can never rely on the order
        • if you want to sort, you have to extract and sort yourself
    • Group related values in structs
      • struct is similar to
        • structs in C
        • classes in Java
      • but no inheritance model
      • capitalize if you want type to be used by other parts of application
        • type Product
      • simple struct
      • structs
        • package main
           
          import "fmt"
           
          func main() {
          emp := Employee{"Hans", "Hansrodt", 34}
          Separator()
          fmt.Println(emp)
          fmt.Printf("Employee: %v\n", emp)
          fmt.Printf("Employee: %+v\n", emp)
          Separator()
          fmt.Printf("The employee %v is %v years old.\n", emp.FirstName + " " + emp.LastName, emp.Age)
          emp.Age = emp.Age + 10
          Separator()
          fmt.Printf("The employee %v is %v years old.\n", emp.FirstName + " " + emp.LastName, emp.Age)
          }
           
          type Employee struct {
          FirstName string
          LastName string
          Age int
          }
           
    • Program conditional logic
      • if doesn't require braces
      • very simple
      • brace has to be on same line, not like in C#
      • basic if
        • package main
           
          import "fmt"
           
          func main() {
           
          appState := "offline"
           
          if appState == "online" {
          fmt.Println("app is online")
          } else if appState == "offline"{
          fmt.Println("app is OFFLINE")
          } else {
          fmt.Println("app state uncertain")
          }
          }
      • another syntax
    • Evaluate expressions with switch statements
      • switch (dont' need breaks)
        • // random number
          dow := rand.Intn(7) + 1
          fmt.Println("Day", dow)
           
          var result string
          switch dow {
          case 1:
          result = "Sunday"
          case 2:
          result = "Monday"
          case 3:
          result = "Tuesday"
          default:
          result = "(some other day)"
          }
      • you can use fallthrough
    • Create loops with for statements
      • four for loops
      • their is continue, break and this goto statement
    • MAKE API
      • api.go (with CORS set as open)
        • package main
           
          import (
          "encoding/json"
          "fmt"
          "net/http"
          "strconv"
          )
           
          func main() {
           
          port := 7788
           
          http.HandleFunc("/languages", func(w http.ResponseWriter, r *http.Request) {
          w.Header().Set("Access-Control-Allow-Origin", "*")
          w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
          w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
           
          w.Header().Set("Content-Type", "application/json")
          json.NewEncoder(w).Encode([]string{"C#", "Java", "Ruby", "Python", "JavaScript"})
          })
          fmt.Printf("listening at http://localhost:%v/languages\n", port)
          http.ListenAndServe(":"+strconv.Itoa(port), nil)
          }
      • React App.tsx
        • import { useEffect, useState } from "react";
           
          function App() {
          const [languages, setLanguages] = useState<string[]>([]);
           
          useEffect(() => {
          (async () => {
          const response = await fetch("http://localhost:7788/languages");
          const _languages = await response.json();
          setLanguages(_languages);
          })();
          }, []);
           
          return (
          <div className="App">
          <h1>Frontend Test</h1>
          <p>There are {languages.length} languages:</p>
          <ul>
          {languages.map((language, index) => {
          return <li key={index}>{language}</li>;
          })}
          </ul>
          </div>
          );
          }
           
          export default App;
      • the Go backend / React frontend works:
    • Define and call functions - 2024-07-02 20:20:43
      • you applicatoin always has a package named main
        • it has a function name main
        • this main function is automatically called at runtime
        • you can make your own functions
        • you always provide a return type
        • if two parameters are the same then just 1x type
    • Define functions as methods of custom types
      • no inheritance as in Java
      • how to make methods
    • Write and read local text files
      • checkError, good idea
      • write and read files
        • package main
           
          import (
          "fmt"
          "io"
          "os"
          "path/filepath"
          )
           
          const dirName = "output"
           
          func createTextFile(idCode, content string) {
          err := os.MkdirAll(dirName, 0755)
          pathAndFileName := filepath.Join(dirName, idCode+".txt")
          file, err := os.Create(pathAndFileName)
          checkError(err)
          _, err = io.WriteString(file, content)
          checkError(err)
          defer file.Close()
          }
           
          func readTextFile(idCode string) {
          pathAndFileName := filepath.Join(dirName, idCode+".txt")
          data, err := os.ReadFile(pathAndFileName)
          checkError(err)
          fmt.Printf(`
          >>> %s
        • %s
        • `, pathAndFileName, data)
          }
           
          func main() {
          colors := []string{"red", "blue", "yellow", "green"}
          for _, color := range colors {
          createTextFile(color, fmt.Sprintf("This is content about the color %s.", color))
          }
          readTextFile("yellow")
          }
      • he also reads as defered
    • Read a text file from the web
      • fetch text from web (still needs JSON.parse)
        • package main
           
          import (
          "fmt"
          "io"
          "net/http"
          )
           
          const url = "https://edwardtanguay.vercel.app/share/skills.json"
           
          func main() {
          resp, err := http.Get(url)
          checkError(err)
           
          fmt.Printf("Response type: %T\n", resp)
          defer resp.Body.Close()
           
          bytes, err := io.ReadAll(resp.Body)
          checkError(err)
           
          content := string(bytes)
          fmt.Print(content)
          }
    • Parse JSON-formatted text
      • the following code fetches a JSON file from the web and creates a HTML file out of it, start with go run main.go tools.go
        • tools.go
          • package main
             
            import (
            "fmt"
            "io"
            "os"
            "path/filepath"
            )
             
            const outputDirName = "output"
             
            func separator() {
            fmt.Println("---")
            }
             
            func checkError(err error) {
            if err != nil {
            panic(err)
            }
            }
             
            func createTextFile(fileName, content string) {
            err := os.MkdirAll(outputDirName, 0755)
            pathAndFileName := filepath.Join(outputDirName, fileName)
            file, err := os.Create(pathAndFileName)
            checkError(err)
            _, err = io.WriteString(file, content)
            checkError(err)
            defer file.Close()
            }
             
            func getHtmlBoilerplateBegin(title string) string {
            return `
            <!DOCTYPE html>
            <html lang="en">
            <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>` + title + `</title>
            <style>
            * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            }
            body {
            font-family: monospace;
            line-height: 1.5;
            padding: 1rem;
            }
            h2 {
            font-size: 1.2rem;
            margin-top: .3rem;
            }
            p {
            font-size: 1rem;
            font-style: italic;
            }
            a {
            color: #333;
            }
            </style>
            </head>
            <body>
            `
            }
             
            func getHtmlBoilerplateEnd() string {
            return `
            </body>
            </html>`
            }
        • main.go
          • package main
             
            import (
            "encoding/json"
            "fmt"
            "io"
            "net/http"
            )
             
            const url = "https://edwardtanguay.vercel.app/share/skills.json"
             
            func main() {
            resp, err := http.Get(url)
            checkError(err)
             
            defer resp.Body.Close()
             
            bytes, err := io.ReadAll(resp.Body)
            checkError(err)
             
            json := string(bytes)
             
            skills := convertJsonToSkills(json)
            htmlContent := convertSkillsToHtml(skills)
            createTextFile("skills.html", htmlContent)
            }
             
            func convertSkillsToHtml(skills []Skill) string {
            html := ""
            title := "Skills"
            html += getHtmlBoilerplateBegin(title)
            html += fmt.Sprintf("<h1>%s</h1>", title)
            for _, skill := range skills {
            html += fmt.Sprintf("<h2><a target=\"_blank\" href=\"%s\">%s</a></h2>\n", skill.Url, skill.Name)
            html += fmt.Sprintf("<p>%s</p>\n", skill.Description)
            html += fmt.Sprintf("\n")
            }
            html += getHtmlBoilerplateEnd()
            return html
            }
             
            func convertJsonToSkills(content string) []Skill {
            var skills []Skill
            err := json.Unmarshal([]byte(content), &skills)
            checkError(err)
            return skills
            }
             
            type Skill struct {
            IdCode string `json:"idCode"`
            Name string `json:"name"`
            Url string `json:"url"`
            Description string `json:"description"`
            }