Go Static Typing 'Magic'

As I understand Go more, some of the concepts tend to make my head hurt. Sometimes, innocent examples in various tutorials hide such deep concepts, that it takes a while for me to decode it all.

Here is an example. In various tutorials, pauses are made using time.Sleep().

The first time I saw an example like the following, it made me stop in my tracks.

package main

import (
	"time"
)

func main() {
	time.Sleep(100 * time.Millisecond)
}

Here is the signature of time.Sleep

func Sleep(d Duration)

And here is what Duration and time.Millisecond means.

type Duration int64

const (
        Nanosecond  Duration = 1
        Microsecond          = 1000 * Nanosecond
        Millisecond          = 1000 * Microsecond
        Second               = 1000 * Millisecond
        Minute               = 60 * Second
        Hour                 = 60 * Minute
)

Coming from my past experience with other languages, my question was, in a strongly typed language like Go:

  1. How does 100 * time.Millisecond work? Aren’t they entirely two different types? And I know that Go doesn’t support operator overloading. So how does Go know how to use the operator * between a Duration and what looks like an int. My first guess was that Go was converting the duration time.Millisecond to an int to make the multiplication work, like in other language.
  2. Even if somehow the multiplication worked, how is the result satisfying the type requirement for the parameter passed to Sleep() which requires a Duration.

Turns out my guess was wrong.

There are a couple of points in the spec that explains what is happening.

  • Since Duration is of type int64, this confirms that a Duration and a int64 are different even though the latter is the underlying type of the former.

    A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it. The new type is called a defined type. It is different from any other type, including the type it is created from.

  • However, type identity is the most crucial part which establishes that int64 and Duration as considered to be identical type whenever the question arises.

    A defined type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types.

  • However, this is the missing link I was looking for. This is how 100 * time.Millisecond becomes time.Duration(100) * time.Millisecond

    For other binary operators, the operand types must be identical unless the operation involves shifts or untyped constants. … Except for shift operations, if one operand is an untyped constant and the other operand is not, the constant is converted to the type of the other operand.

  • And what happens after the multiplication? The result is considered to be of type time.Duration

    Arithmetic operators apply to numeric values and yield a result of the same type as the first operand.

So, if I have got it right:

  1. Go will convert the untyped integer value 100 to the type Duration.
  2. Since both have the underlying type of integer, it will follow the integer operator rules for *
  3. The result will be converted to the type of time.Duration
 
comments powered by Disqus