Posts
-
[Some Maps and Gold Pun Here]
January 8, 2019
It’s Maps now and there are a few notable nuggets here too.
NUGGET:
A map lookup using a key that isn’t present returns a zero value for its type The Go Programming Language (2016) Donovan and Ritchie, pp. 94
Nice, but how do we tell if a map element is really there or not? Accessing elements actually returns a tuple, the first element of which is the element value (or zero value) and the second is a Boolean. The Boolean is
true
if the provided key matched, andfalse
if you’ve just got the default, empty value. That seems pretty cool and something I can imagine will be useful.NUGGET:
A map element is not a variable, and we cannot take it’s address. The Go Programming Language (2016) Donovan and Ritchie, pp. 94
The reason for this is the map might need to rehash, and move things around for its own purposes. If you have a memory reference to an element, this will most likely then point to nothing / the wrong thing after the rehashing. To avoid this, you’re not allowed to use the
&
operation to get the pointers to the elements.This one is more of a :thinkingface: for me. I’m still trying (struggling?) to see the benefits of pointers. They seem like a break in encapsulation. But then I know from Scala / functional circles that not everyone things of encapsulation as the Holy Grail of programming. Pushing myself out of my comfort zone is why I’m on this journey so I’m going to store it in the memory banks and move on.
NUGGET:
As with Slices, Maps cannot be compared with each other. Yet again, the only legal comparison is with
nil
.It seems again that this is what I consider a core library leaving me with a bunch of work to do, caused by design decisions elsewhere in the language. It feels like a loss to not have this, but then I’ve not really come across the potential benefits which might be significant. At this point however it feels as if my cognitive load when working with Maps is going to be higher than I’m used to. And it’s not just Maps;
NUGGET:
Go does not provide a set type, but since the keys of a map are distinct, a map can serve this purpose. The Go Programming Language (2016) Donovan and Ritchie, pp. 96
This seems really wierd to me. At this stage the standard collections support feels very strange. We don’t have a Stack implementation either as I learned earlier in my reading. It really is a strange new world this Goland [sic].
-
Encountering Slices
January 8, 2019
Slices currently feel like the solution to a problem I’ve not yet felt the pain of. That’s not to say the problem isn’t there; I’m just not aware of it yet. Is this because in systems programming the data you’re close to and manipulate more is of a different kind? Or of a different structure? Or more unpredictable? Or more raw? Or is performance more a concern from the outset? I guess I’ll have to wait and see.
For my learning purposes, what follows is a series of facts and musings about them, strung together in some kind of train-of-thought type structure;
Literals are initialised without a size (unlike array literals).
They can extend (within the cap).
They’re not comparable out of the box (so we can’t use
==
). While the standard golang libraries provide a function to compare byte slices thats the only one; the rest you need to build yourself.Is this because the rest aren’t that frequently needed? I’ll have to wait and find out.Digging into why you can’t directly compare them is instructive. Firstly;
Unlike array elements, the elements of a slice are indirect, making it possible for a slice to contain pointers to contain itself. The Go Programming Language (2016), Donovan and Ritchie, pp. 87
And secondly;
because slice elements are indirect, a fixed slice may contain different elements at different times as the content of the underlying array are modified. The Go Programming Language (2016), Donovan and Ritchie, pp. 87
Let’s be explicit about what “indirect” is. Slices are like windows onto a subset of an underlying data type - an Array. Without Arrays they don’t exist. They don’t have any storage themselves - they just contain a pointers to an Array - and then add on characteristics such as
len
andcap
.It may be instructive at this point to bring in some ways of categorising things.
Composite types are types created by combining the basic types (Integers, String etc.) in various ways.
Arrays (and Structs) are a subset of Composite Type, called Aggregate Types . They are concatenations of other values in memory.
Slices, while they are Composite Types are not Aggregate Types, and must have zero or one underlying Array type behind them. If a Slice has no underlying Array type then checking against
nil
with the comparison operator==
(the only usage of this operator valid for Slices) will returntrue
. This is the zero-value of Slices.A slice can still be empty if it has an underlying array, but the
len
will then be zero.To use slices directly its important to bear in mind that although the elements of the underlying array are indirect , the slices pointer,
len
andcap
are not. The Go Programming Language (2016), Donovan and Ritchie, pp. 91Which leads me to a question which I intend to explore in a later post; do slices need to point to contiguous elements of their undelying array? I assume so, but it’s always best never to assume I’ve found.
Addendum
Slices can be used to implement stacks. But why not just have a Stack data type? It seems strange coming from the Java language with such a rich set of collections types, to arrive in goland [sic] where I find myself with just a pen knife and a compass. It’s different, but thats why I’m here; to find out why.
-
`x`, `&x` and `*x`; what's the difference?
January 4, 2019
This was a longer post; But I lost it. This is a summary to flush what remains from my mental buffers.
Pointers and pointer arithmatic is new to meAlthough I’m more than conversant with references, the poorer cousin from Javaland so grokking them as and when they come up is important.
There’s a lot in the two appearances of the concept in Donovan and Ritchie’s The Go Programming Language (2016)On page 24, and again on pages 32-34. I wanted to go slightly slower than they did, and make a few points super-clear.
They show us what’s happening with this code (page 32):
x := 1 p := &x // p(ointer) of type *int (pointer to an int). Points to x fmt.Println(*p) // prints "1"
When I first read this I thought (incorrectly) that
&x
and*x
were variables. Wrong. Thex
here is the variable. The&
is an operator, which obtains the pointer value - the memory address. We can start with our printing again and prove it:x := 1 fmt.Println(x) // prints "1" fmt.Println(&x) // prints "0xc0000180a0"
The other misconception I had was that
*x
acted onx
. It doesn’t. If you try that you get a compilation error (which makes a lot more sense when you realise the*
is also an operator). You need to call it on a pointer type (e.g. of type*int
) and it will give you back the variable that this points to. Let’s do some more printing, starting at the beginning yet again, and prove it:x := 1 fmt.Println(x) // prints "1" fmt.Println(&x) // prints "0xc0000180a0" fmt.Println(*x) // prints "1" p := &x // p(ointer) of type *int (pointer to an int). Points to x fmt.Println(p) // prints "0xc0000180a0" fmt.Println(*p) // prints "1"
So to summarise, pointers are a single new thing, which you can work with using two operators; the first,
&
, gets the pointer of type*type
from the variable. The second,*
gets the variable from the pointer.As a conclusion, there is one thing which doesn’t sit entirely well with me and that’s the fact that
&
gets you something of type*type
rather than&type
. It’s a small thing, but may have been at the root of my mis-grokking of all this. -
Goroutines, I presume
January 3, 2019
It’s in Chapter 1 of Donovan and Ritchie’s “The Go Programming Language” (2016) that we first encounter the famous goroutine.
We learn that everything we’ve done leading up to this point has been running within a goroutine - the default,
main
one. Suddenly we see we can create more using thego
keyword. That’s cool, but on their own, goroutines can never be super-useful. When we connect them together however; then all kinds of madness could ensue.We do this connecting via channels which we create using the keyword
make
.Not to be confused with the UNIXmake
command. Channels allow us to pass values (NOTE: not references) of a specified type to other goroutines.To me, the syntax seemed a little hairy at first sight, and that’s what I’m going to pick at a tiny bit in this post.
We’re told that:
when one goroutine attempts to send or receive on a channel, it blocks until another goroutine attempts the corresponding receive or send operation, at which point the value is transferred and both goroutines proceed. Donovan and Ritchie, The Go Programming Language, (2016) pp.18
In order to achive this we have to “
make
” our channelch := make(chan string)
We also have to create new goroutines
go fetch(url, ch) // start a goroutine
And from each of these new goroutines send a value on channel
ch
ch <- fmt.Sprint(err) // send to channel ch
Which are received (and printed) by channel
main
fmt.Println(<-ch) // receive from channel ch
So why do I say “hairy”? I’ll admit, I used to hate symbols like “
<-
” and “<=
“Back in my Scala days. but I got over that. What seems a little arbitrary hereperhaps I’m just being hyper-sensitive knowing thatgofmt
is watching my every move.” is the space before the arrow in constructs likech <-
but the lack of one after it, for example, in<-ch
. It feels to me like they should both be either one thing or the other format-wise.However, I’ve been on the programming block long enough to know that differences like this are rarely accidental. Rather, they typically point to something far more fundamental which I’m just not aware of yet. I’m going to keep my eye on things as I read on, and follow up this post if / when I discover anything pertinent.
-
Hello 世界
December 31, 2018
I’m starting my Golang journey with Donovan and Kernighan’s “The Go Programming Language” (2016). Why? Becuase it’s a text which not only starts at the point where I currently find myself skills-wise (long-term dev experience with Java and heavy sojourns into other languages but nothing to speak of in the systems-programming space) but it also reminds me of the first ever programming book I bought; “The C Programming Language” by Kernighan and Ritchie. It made little sense to me at the time, and the C code I wrote was terrible, but the nostalgia is still strong, and Kernighan (and Donovan) can write on languages very clearly.
Donovan and Kernighan start their journey into Golang with the standard “Hello …” construct, and immediately there is a lot to take note of.
The Golang Toolchain
Golang is compiled, which you achieve by a combination of the
go
command and certain sub-commands. There are many of them, but here we seerun
andbuild
(which builds a platform-specific executable that can be run without thego
environment being installed.)Unicode
You can see from the title of this post that we’re bidding hello in Japanese. Support for Unicode is native in Golang. Nothing new having come from the Java world, but nice to know.
Packages and Standard Libraries
Everygthing is packaged. Our code is packaged, and so are the Standard Libraries which are
import
ed. So far so unexpected.However there is a concept that is new to me; there is a special package called
Main
which contains entry points to your Golang programs. When complied,Main
packages create standalone, executable programs.Clean Syntax
No semicolons required!
Clean Style
It is Golang style to run
gofmt
andgoimports
on your code. That ensures all Golang code looks the same, and that there are no rogue (i.e. unused) imports.Go-Managed, Easy-to-Use Toolchain
We don’t have the
goimport
tool for the toolchain in the default install, so we justgo get
it and it’s available to use. -
Things of Note - Part 1
December 29, 2018
High-Level
As a newer language, Golang benefits from many language developments which went before it. I pull out some of these which are of particular note in separate sections below, but the “just plain handy” list includes: a package system, first-class functions, lexical scope, a system-call interface, and immutable strings (from The Go Programming Language, pp xiv by Donovan, A. and Kernighan, B.). It also includes in the platform linting, dependency management, testing, packaging and more.
Presumes monorepo-style SCM
Optimised for large teams
Managed runtime
Golang takes note of the development in managed runtimes which found popularity with the development of languages such as Java and C# (and their many JRE- and CLR- platform cousins). Golang also has a managed runtime. That means you get garbage collection which is a major boon. The downside is that it’s not suitable for real-time projects (although I remember real-time Java so I’m not sure these kind of things are impossible; rather they are likely to require a different style of coding.)
Cross-platform
The Golang platform is available on many platforms (Unix, MacOs, and Windows) and chip architectures (including ARM). This goes a long way towards ensuring that your code is very likely to be compilable on multiple platforms unchanged.
Time-to-Compile
Golang is a compiled language. But not only that, the designers have focussed on keeping compile-time as low as possible. If you’re coming from a Scala background, that’s reassuring.
Bundling dependencies
Network-Centric
Golang “takes note of the importance of locality” (from The Go Programming Language, pp xiv by Donovan, A. and Kernighan, B.) but also has first-class concepts for remote communication.
NOT Object-Oriented
-
Pros and Cons (IMHO) - Part 1
December 29, 2018
Pros
There is only one right way to do things, enforced by
gofmt
.It is very much a toolchain and a platform as well as a language.
Cons
IDE support is really nascent - time to learn EMACS (finally).
Installation feels really complicated (coming from a Java background) and Ben Boyter agrees with me.
-
Setting Up
December 28, 2018
Hardware, OS and Shell
I’m using a MacBook Pro which is running MacOS Mojave (10.14.2). My shell of choice is Z, made all glorious by oh-my-zsh.
Installation
I downloaded the latest binary release (
go1.11.4
) from the Golang site and installed it at the default location (/usr/local/go
).I tested my install by following the documentation at golang.org.
Configuration
Based on a recommendation from a friend I started to follow the steps laid out in “How to Start a Go Project in 2018”.
This entailed;
Explicitly setting the
GOPATH
environment variable explicitly (I addedexport $GOPATH="/Users/[my username]/go"
to the bottom of my~/.zshrc
file),Adding the
$GOPATH/bin
directory to my$PATH
(I addedexport PATH="$PATH:$(go env GOPATH)/bin"
to the bottom of my~/.zshrc
file).It seems that the defaults of this setup has changed over the course of Golang’s evolution because an old book I have from 2012; “The Way to Go” by Ivo Balbert; mentions a bunch of other variables which are still in the go environment but seem to have less precedence. Ivo also wants me to have my Golang programs in a different location, and finally, he kicks everything off by cloning the language source and building it. Luckily this isn’t mandatory.
IDE
At the time of writing, it seems as if Visual Studio code has the best free support for Golang. I’ve installed that and will be trying to use it as my primary dev environment.
However, I’m typically a JetBrains user, so I might end up installing their Go plugin too if things-Msft prove to be a brain melt.