Lexical elements: Imaginary literals
January 20, 2023
I understand in principle what imaginary numbers are. I used them during high school and university mathematics classes to get good grades.
I’ve never had a practical use for them.
I’ve certainly never used them in programming, in Go or otherwise.
All that said, I guess it’s cool that Go has first-class support for imaginary numbers, rather than it being an add-on library of some kind.
If you’re like me, and have never had the need to write software that understands complex numbers, you might skip today’s email, or better yet, jump over to 3Blue1Brown’s video that explains what complex and imaginary numbers are, and why they’re useful.
Imaginary literals
An imaginary literal represents the imaginary part of a complex constant.
What’s important to understand from this opening sentence is that imaginary literals are not meant to be used alone. They’re meant to be used in creating complex values or constants. So while you could pass around an imaginary value in your code willy nilly, it won’t generally do you any good without a more complete understanding of complex number theory, and how Go interacts with complex numbers, the latter of course, which we’ll get to in time.
It consists of an integer or floating-point literal followed by the lowercase letter
i
. The value of an imaginary literal is the value of the respective integer or floating-point literal multiplied by the imaginary uniti
.imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
For backward compatibility, an imaginary literal’s integer part consisting entirely of decimal digits (and possibly underscores) is considered a decimal integer, even if it starts with a leading
0
.
This paragraph is really the only “gotcha”. As discussed earlier this week normally 0123
would be interpreted as the Octal value 0o123
(or 83
in decimal), a historical exception is made for imaginary literals, such that a leading 0 is still treated as a decimal value.
0i 0123i // == 123i for backward-compatibility 0o123i // == 0o123 * 1i == 83i 0xabci // == 0xabc * 1i == 2748i 0.i 2.71828i 1.e+0i 6.67428e-11i 1E6i .25i .12345E+5i 0x1p-2i // == 0x1p-2 * 1i == 0.25i
Quotes from The Go Programming Language Specification, Version of June 29, 2022
Related Content

Empty structs
We finally we have enough knowledge for the EBNF format not to seem completely foreign, so let’s jump back and take a look at that, with the examples provided in the spec… Struct types … StructType = "struct" "{" { FieldDecl ";" } "}" . FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] . EmbeddedField = [ "*" ] TypeName [ TypeArgs ] . Tag = string_lit . // An empty struct.

Struct tags
Struct types … A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored. struct { x, y float64 "" // an empty tag string is like an absent tag name string "any string is permitted as a tag" _ [4]byte "ceci n'est pas un champ de structure" } // A struct corresponding to a TimeStamp protocol buffer.

Struct method promotion
Yesterday we saw an example of struct field promotion. But methods (which we haven’t really discussed yet) can also be promoted. Struct types … Given a struct type S and a named type T, promoted methods are included in the method set of the struct as follows: If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.