aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2020-08-01 12:07:25 +0100
committerAkshay <[email protected]>2020-08-01 12:07:25 +0100
commit6dc5eb710c27a3fee4a132763c106c5ce16f72ab (patch)
tree39c8914e039bad82f6bf5741d9b5743cdfcf460f
parentcc72d07ae82386e9f087517ccf2653e17c76714a (diff)
add new post: Gripes with Go
-rw-r--r--posts/gripes_with_go.md177
1 files changed, 177 insertions, 0 deletions
diff --git a/posts/gripes_with_go.md b/posts/gripes_with_go.md
new file mode 100644
index 0000000..60aabc8
--- /dev/null
+++ b/posts/gripes_with_go.md
@@ -0,0 +1,177 @@
1You've read a lot of posts about the shortcomings of the Go
2programming language, so what's one more.
3
4 1. [Lack of sum types](#lack-of-sum-types)
5 2. [Type assertions](#type-assertions)
6 3. [Date and Time](#date-and-time)
7 4. [Statements over Expressions](#statements-over-expressions)
8 5. [Erroring out on unused variables](#erroring-out-on-unused-variables)
9 6. [Error handling](#error-handling)
10
11### Lack of Sum types
12
13A "Sum" type is a data type that can hold one of many states
14at a given time, similar to how a boolean can hold a true or
15a false, not too different from an `enum` type in C. Go
16lacks `enum` types unfortunately, and you are forced to
17resort to crafting your own substitute.
18
19A type to represent gender for example:
20
21```go
22type Gender int
23
24const (
25 Male Gender = iota // assigns Male to 0
26 Female // assigns Female to 1
27 Other // assigns Other to 2
28)
29
30fmt.Println("My gender is ", Male)
31// My gender is 0
32// Oops! We have to implement String() for Gender ...
33
34func (g Gender) String() string {
35 switch (g) {
36 case 0: return "Male"
37 case 1: return "Female"
38 default: return "Other"
39 }
40}
41
42// You can accidentally do stupid stuff like:
43gender := Male + 1
44```
45
46The Haskell equivalent of the same:
47
48```haskell
49data Gender = Male
50 | Female
51 | Other
52 deriving (Show)
53
54print $ "My gender is " ++ (show Male)
55```
56
57### Type Assertions
58
59A downcast with an optional error check? What could go
60wrong?
61
62Type assertions in Go allow you to do:
63
64```go
65var x interface{} = 7
66y, goodToGo := x.(int)
67if goodToGo {
68 fmt.Println(y)
69}
70```
71
72The error check however is optional:
73
74```go
75var x interface{} = 7
76var y := x.(float64)
77fmt.Println(y)
78// results in a runtime error:
79// panic: interface conversion: interface {} is int, not float64
80```
81
82### Date and Time
83
84Anyone that has written Go previously, will probably already
85know what I am getting at here. For the uninitiated, parsing
86and formatting dates in Go requires a "layout". This
87"layout" is based on magical reference date:
88
89```
90Mon Jan 2 15:04:05 MST 2006
91```
92
93Which is the date produced when you write the first seven
94natural numbers like so:
95
96```
9701/02 03:04:05 '06 -0700
98```
99
100Parsing a string in `YYYY-MM-DD` format would look something
101like:
102
103```go
104const layout = "2006-01-02"
105time.Parse(layout, "2020-08-01")
106```
107
108This so-called "intuitive" method of formatting dates
109doesn't allow you to print `0000 hrs` as `2400 hrs`, it
110doesn't allow you to omit the leading zero in 24 hour
111formats. It is rife with inconveniences, if only there were
112a [tried and
113tested](https://man7.org/linux/man-pages/man3/strftime.3.html)
114date formatting convention ...
115
116### Statements over Expressions
117
118Statements have side effects, expressions return values.
119More often than not, expressions are easier to understand at
120a glance: evaluate the LHS and assign the same to the RHS.
121
122Rust allows you to create local namespaces, and treats
123blocks (`{}`) as expressions:
124
125```rust
126let twenty_seven = {
127 let three = 1 + 2;
128 let nine = three * three;
129 nine * three
130};
131```
132
133The Go equivalent of the same:
134
135```go
136twenty_seven := nil
137
138three := 1 + 2
139nine := three * three
140twenty_seven = nine * three
141```
142
143
144### Erroring out on unused variables
145
146Want to quickly prototype something? Go says no! In all
147seriousness, a warning would suffice, I don't want to have
148to go back and comment each unused import out, only to come
149back and uncomment them a few seconds later.
150
151### Error handling
152
153```go
154if err != nil { ... }
155```
156
157Need I say more? I will, for good measure:
158
1591. Error handling is optional
1602. Errors are propagated via a clunky `if` + `return` statement
161
162I prefer Haskell's "Monadic" error handling, which is
163employed by Rust as well:
164
165```rust
166// 1. error handling is compulsory
167// 2. errors are propagated with the `?` operator
168fn foo() -> Result<String, io::Error> {
169 let mut f = File::open("foo.txt")?; // return here if error
170 let mut s = String::new();
171
172 f.read_to_string(&mut s)?; // return here if error
173
174 Ok(s) // all good, return the value inside a `Result` context
175}
176```
177