Skip to content

Instantly share code, notes, and snippets.

@progrium
Created Feb 17, 2015
Embed
What would you like to do?
Comparing natural brevity and concise expressiveness between Go and shell/Bash for certain tasks using each tool's "standard" library.
curl -s "$url" | tar -zxC "$dest"
func downloadAndInstall(url, dest string) error {
resp, err := http.Get(url)
if err != nil {
return error
}
defer resp.Body.Close()
zip, err := gzip.NewReader(resp.Body)
if err != nil {
return error
}
defer zip.Close()
archive := tar.NewReader(zip)
for {
header, err := archive.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
filename := fmt.Sprintf("%s/%s", dest, header.Name)
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err := io.Copy(file, archive)
if err != nil {
return err
}
}
}
@alessiodm
Copy link

alessiodm commented Feb 17, 2015

The comparison is impressive at the first glance, but I think it is a little bit unfair: we are comparing a higher-level declarative style of coding with a finer-grained imperative code. I think we can reach a similar brevity in Go if we introduce some concepts like streams, etc. and we write some higher-level functions handling that.

In the example above, for example, we have the ability to catch an error and handle it in Go, while the bash script probably just crashes or has a behavior with (uncontrolled/unexpected?) side-effects. The control/safety/behavior of the code is also another (hidden) factor in the comparison, that I loved, but perhaps it's hiding too many relevant considerations (even if it is not claiming that one is better than the other) :)

@andys
Copy link

andys commented Feb 17, 2015

I respectfully submit that you should write the corresponding program in C. That is the comparison that I think about most often when using Go 😉

@kef
Copy link

kef commented Feb 18, 2015

Would like to see examples of how the Go version could be improved, using the typical practices of an experienced Go programmer. Any takers?

@cablehead
Copy link

cablehead commented Feb 18, 2015

The bash example would need 'set -o pipefail' to replicate the error handling in the Go version through the HTTP download.

@msabramo
Copy link

msabramo commented Feb 18, 2015

This is what I struggle with in Go. I feel it has some interesting ideas, but it's not very nice to read.

This is I prefer Python. But I'd be curious to see how to improve the Go code to make it more succinct.

@abevoelker
Copy link

abevoelker commented Feb 18, 2015

@cablehead set -o pipefail just deals with error code propagation. You can't replicate the immediate abort-on-failure of the Go code using just pipes (i.e. tar will still run no matter what w/ a pipes solution). You'd have to write a full script w/ separate commands to avoid running tar if curl errored.

@cablehead
Copy link

cablehead commented Feb 18, 2015

@abevoelker Thanks for the clarification. Oof, yeah, my understanding was that set -o pipefail would abort if the curl fails, before the tar runs. You're right though:

andy@hop:~/tmp$ cat 1.sh
#!/bin/bash

false | echo "foo"
echo $?

andy@hop:~/tmp$ ./1.sh 
foo
0

andy@hop:~/tmp$ cat 2.sh
#!/bin/bash

set -o pipefail

false | echo "foo"
echo $?

andy@hop:~/tmp$ ./2.sh
foo
1

@cablehead
Copy link

cablehead commented Feb 18, 2015

I definitely see @progrium's point, but in that case, once both versions are safe, the bash version wouldn't be as clearly terse.

@cablehead
Copy link

cablehead commented Feb 18, 2015

@msabramo Python's concurrency story is woeful though, and I fear that asyncio is a long winded wrong turn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment