Short Variable Declarations Are Oversold

271 views
Skip to first unread message

jlfo...@berkeley.edu

unread,
Apr 22, 2023, 6:31:11 PM4/22/23
to golang-nuts

As a beginning Go programmer, I was initially attracted to the short variable declaration syntax.


It’s great for declaring variables of a simple type. What could be wrong with letting the Go compiler infer a variable type by looking at what’s being assigned to the variable, such as:


func main() {

x := 10

}


Nothing. This is trivially the same as


func main() {

var x int

x = 10

}


I didn’t have to waste my precious time by entering an explicit type declaration for “x” when the compiler already knew what it was.


But what about something like


import “os”

func main() {

     file, _ := os.Open(os.Args[1])

}


Again, nothing is wrong, at least not until I have to pass “file” to another function. For example


import “os”

func main() {

     file, _ := os.Open(os.Args[1])

myfunc(file)

}


func myfunc(file ???) {

}


What type should I use to declare “file” in the parameter list for myfunc()? As a new Go programmer I have to admit that I haven’t memorized all the types used in the Go standard library. So, I have to break off working on myfunc() to look up the type returned by os.Open(). This isn’t a huge deal, but it’s a distraction and can waste time. I suppose that as I gain experience with Go, I’ll have to do this less and less.


But, it doesn’t matter how experienced I am with Go when I start using user-defined types from packages that aren’t in the Go standard library. For example (from Ben Hoyt’s excellent goawk program):


import "github.com/benhoyt/goawk/parser"

func main() {

prog, err := parser.ParseProgram(fileReader.Source(), parserConfig)

myfunc(prog)

}


func myfunc(prog ???) {

}


Since I’m going to have to look up the returned types of parse.ParseProgram anyway, what’s the advantage of using a short variable declaration? They just delay the inevitable.


Short definitions detract from one of Go’s primary goals - readability. I started using Go in the first place because I wanted a strongly typed language with explicit type declarations. 


As a result of all this, I’ve started to avoid short variable declarations, except when I’m initializing a variable inside an "if", "for", or "switch” statement, such as


if v := math.Pow(x, n); v < lim {

}


for i := 0; i < 10; i++ {

}


switch os := runtime.GOOS; os {

}


In these cases I have no choice if I want to declare local temporary variables since long declarations aren’t syntactically allowed.


I doubt if this note will change anybody’s mind but it’s something to think about.

Message has been deleted

Dan Kortschak

unread,
Apr 22, 2023, 7:09:50 PM4/22/23
to golan...@googlegroups.com
On Sat, 2023-04-22 at 15:31 -0700, jlfo...@berkeley.edu wrote:
> What type should I use to declare “file” in the parameter list for
> myfunc()? As a new Go programmer I have to admit that I haven’t
> memorized all the types used in the Go standard library. So, I have
> to break off working on myfunc() to look up the type returned by
> os.Open(). This isn’t a huge deal, but it’s a distraction and can
> waste time. I suppose that as I gain experience with Go, I’ll have to
> do this less and less.

LSP will let you know, but to provide a counterpoint... The type that
the function should take may not be the concrete type, func myfunc(file
...) might be func myfunc(f *os.File) or func myfunc(r io.Reader) or
any of a number of other interfaces that *os.File satisfies. The choice
here is not necessarily defined by the type, but more by the intention
of the programmer.

Bakul Shah

unread,
Apr 22, 2023, 11:04:14 PM4/22/23
to jlfo...@berkeley.edu, golang-nuts
If you are not using an IDE, you can always use, for example, "go doc os.Open" to see what os.Open returns.

For user packages, you can use for example 
"go doc github.com/benhoyt/goawk/parser.ParseProgram" from your directory that contains "go.mod" for your program.

Shulhan

unread,
Apr 23, 2023, 8:26:10 AM4/23/23
to jlfo...@berkeley.edu, golang-nuts

23 Apr 2023 05:31:25 jlfo...@berkeley.edu <jlfo...@berkeley.edu>:


> Short definitions detract from one of Go’s primary goals - readability. I started using Go in the first place because I wanted a strongly typed language with explicit type declarations. 
>
> As a result of all this, I’ve started to avoid short variable declarations, except when I’m initializing a variable inside an "if"[https://go.dev/ref/spec#If_statements], "for"[https://go.dev/ref/spec#For_statements], or "switch[https://go.dev/ref/spec#Switch_statements]” statement, such as
>
...
>
> I doubt if this note will change anybody’s mind but it’s something to think about.
>

I agree with OP and probably this is one of the unpopular opinion about Go style.

My reasoning is based on three things [1]. First, readability, as also pointed out by OP.  Given the following statement

t := f()

If you are new to code base, you need to know the signature of f to know the type of t. To minimize back and forth when reading code, it is better to declare the type of t before or along with assignment. Yes, some advanced IDE may provide clickable link, unfortunately not all of us use IDE when developing Go.

Second, minimize local, temporary variables.

Third, prevent subtle bugs [2][3][4] caused by shadowing.

For a long term code base, I try to enforce the variable to be declared explicitly with downside more line of codes.

[1] https://kilabit.info/journal/2017/05/Go_Informal_Coding_Style#avoid______if_possible
[2] https://github.com/golang/go/issues/377
[3] https://github.com/golang/go/issues/20733
[4] https://github.com/golang/go/issues/21291

Jesper Louis Andersen

unread,
Apr 23, 2023, 9:09:15 AM4/23/23
to jlfo...@berkeley.edu, golang-nuts
On Sun, Apr 23, 2023 at 12:31 AM jlfo...@berkeley.edu <jlfo...@berkeley.edu> wrote:


Short definitions detract from one of Go’s primary goals - readability. I started using Go in the first place because I wanted a strongly typed language with explicit type declarations. 



Your claim of readability is not held by everyone. Some people prefer there be no type information in a program because the type information "detracts from what the program is doing". Hence, it becomes rather hard to please everybody.

Short variable declarations are a poor man's type inference. In fully type-inferred languages, you can omit types everywhere, and the compiler will deduce an appropriate type for each declaration. It will typically pick the most general type for an expression. The type information is still there, but it is generated on-demand by the compiler, and programs which fail the type check are rejected. Haskell and OCaml are good examples of programming languages following this style. Yet in both languages, you often see type declarations sprinkled throughout the code base to guide the reader. You sort-of assume a certain amount of experience, and add types as you see fit to capture that experience. Often, you end up with your interfaces being type-annotated, but your internal code avoiding annotation.

The grand advantage of type inference is that the types can vary easily. If you change a fundamental type, the compiler will check that your change is sound. And you don't have to go around the code base and change every occurrence. That's a really nice boon.

We are slowly moving into a world where the compiler and the programmer are working on the code at the same time. You ask the compiler to fill out gaps in the programs you are writing. The result is that your editor can live-annotate the appropriate types of declarations and expressions because it can be lifted from the compiler. When I write OCaml, for instance, my editor annotates functions with types for me by adding a line above the function declaration in a smaller font. These lines only occur virtually in the buffer, and aren't present in the program file.

For some languages, such as Agda, the interaction is even stronger: you can ask the compiler to fill in parts of the program based on the types they have. That is, types and terms coalesce and there is no stratification between them. Writing a term makes the compiler deduce the type. Writing a type makes the compiler deduce and fill in the term. Coming strong into this are large language models from machine learning. You can fill in lots of gaps in programs via LLMs. Programming often contains a lot of janitorial tasks around a computational kernel and LLMs can accelerate the janitor. In the future, I hope someone takes an LLM and starts exploiting type information. I have a hunch it's going to be far more effective for languages which have static type systems (inferred or not) because there's a much richer set of information you can exploit.



--
J.

Axel Wagner

unread,
Apr 23, 2023, 9:28:26 AM4/23/23
to Jesper Louis Andersen, jlfo...@berkeley.edu, golang-nuts
Just to nit-pick everyone: Short variable declarations are not there to omit type information. You can do that with a regular variable declaration:
Short variable declarations exist to 1. be shorter and 2. allow you to avoid re-declaration errors when assigning multiple variables:
So, IMO short variable declarations definitely increase readability, just by that latter effect. Type-inference is a bonus.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAGrdgiVd3BMOKE6ohRbwTmC_AhSY3Zht4LxK%3DFQjqj_YocoZAg%40mail.gmail.com.

Robert Engels

unread,
Apr 23, 2023, 10:02:02 AM4/23/23
to Axel Wagner, Jesper Louis Andersen, jlfo...@berkeley.edu, golang-nuts
Personally that syntax has always bothered me with readability. It requires lots of previous knowledge in some cases. A syntax like

x, var y int = blah

Is more explicit that x is reused and y is declared. 

Go is all about being explicit until it isn’t. 



On Apr 23, 2023, at 8:28 AM, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:



Robert Engels

unread,
Apr 23, 2023, 10:04:13 AM4/23/23
to Axel Wagner, Jesper Louis Andersen, jlfo...@berkeley.edu, golang-nuts
To clarify, the semantics of a go statement from a language perspective changes based on earlier statements. That is simply weird - and hurts readability. 

On Apr 23, 2023, at 9:01 AM, Robert Engels <ren...@ix.netcom.com> wrote:



Axel Wagner

unread,
Apr 23, 2023, 10:07:39 AM4/23/23
to Robert Engels, Jesper Louis Andersen, jlfo...@berkeley.edu, golang-nuts
On Sun, Apr 23, 2023 at 4:01 PM Robert Engels <ren...@ix.netcom.com> wrote:
Personally that syntax has always bothered me with readability. It requires lots of previous knowledge in some cases. A syntax like

x, var y int = blah

Is more explicit that x is reused and y is declared. 

That specific suggestion would not work. It would mean it is ambiguous whether `var x, err = fmt.Println()` is mean to be a declaration of only x, or both - and note that this specific order is the more relevant, as `err` is the specific variable that tends to be re-used by `:=`.
There probably could be alternatives though. But that seems a moot point now.

Robert Engels

unread,
Apr 23, 2023, 10:11:57 AM4/23/23
to Axel Wagner, Jesper Louis Andersen, jlfo...@berkeley.edu, golang-nuts
I wouldn’t have don’t it that way. I would have made := mean all variables must be new. And = mean any new variable requires a decoration. 

So in this case,

var x,err = means x is new and err is reused. The variable clauses are segregated by a ,  



On Apr 23, 2023, at 9:07 AM, Axel Wagner <axel.wa...@googlemail.com> wrote:



tapi...@gmail.com

unread,
Apr 23, 2023, 9:32:44 PM4/23/23
to golang-nuts

Type inference is not the main purpose (or even the purpose) of short declarations.
I do agree that the negative impact of short declarations is larger than its positive impact,
for reasons different from the one you described. Some reasons:
* short declarations causes confusions (esp, for new gophers) at some situations.
* short declarations overlap too much functionalities with normal declarations.
* short declarations indeed hurt readabilities at some situations (but not the ones as you described).

a2800276

unread,
Apr 24, 2023, 3:13:49 AM4/24/23
to golang-nuts

import “os”

func main() {

     file, _ := os.Open(os.Args[1])

myfunc(file)

}


func myfunc(file ???) {

}


What type should I use to declare “file” in the parameter list for myfunc()?


This argument doesn't seem logical to me. If I fully declare `file` in this case, I also have to know which type to declare it as:

     var file ???;
    file, _ = os.Open(...)

This will also be a "distraction and waste time". I'm all for explicit declarations in cases where they add clarity, but in idiomatic cases, I don't feel it adds anything useful.

Additionally, in cases where I am creating a new function to work with the variable, surely I'm aware of more context concerning the API, e.g. in the example, I'll assume `file` has `Read` and `Close`, but passing the variable along, I would assume the operation on the variable will be more involved and requiring reading the documentation anyway ...

     -tim





 

Robert Engels

unread,
Apr 24, 2023, 7:53:18 AM4/24/23
to a2800276, golang-nuts
If you use Go’s “accept interfaces return concrete types” the dev/ide knows to declare the concrete type. 

Type inference can actually cause more issues during refactorings - not alleviate them. 

Personally, I find types make the code easier to read. But when I write ago I don’t use them. I find when I review old code it is a bit harder.

In Java I think it is less of an issue (on refactoring) because you normally use the interface and due to implements you have more safety. 

On Apr 24, 2023, at 2:14 AM, a2800276 <a280...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages