Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I recently started learning to Go, and I am quite confused as to why it has a strange "non-traditional" syntax unlike other languages (C, C++, C#, JAVA)
For example, a code like this in Go:
package main
import "fmt"
func sum(a int, b int) int {
return a + b
}
func main() {
numbers := [4] int {1, 2, 3, 4}
for _,n := range numbers {
result := sum(n, 2)
fmt.Println(result)
}
}
But, could be written something like in some languages:
package main
import "io"
int sum(int a, int b) {
return a + b
}
void main() {
int numbers[4] = {1, 2, 3, 4}
foreach (n in range(numbers)) {
result = sum(n, 2)
io.print(result)
}
}
So my question is, is there any technical reason behind this syntax, or is it just a personal preference of the team? Especially that the team behind Go used "C Language" to write Go, which means it would've made much more sense to type it in C-Style syntax ?
Few points that I'd like to highlight:
Go is inspired by many languages and not just C.
C: statement and expression syntax
Pascal: declaration syntax
Modula 2, Oberon 2: packages
CSP, Occam, Newsqueak, Limbo, Alef: concurrency
BCPL: the semicolon rule
Smalltalk: methods
Newsqueak: <-, :=
APL: iota
There are more
From when foreach and range become C-style syntax?
Third, don't confuse "For" statements with for clause and range clause. Read the spec.
In Go, you can do this is as well:
for i := 0; i < len(numbers); i++
But range clause is much more powerful once you understand it and yes it is not strange syntax. I'd suggest to read the spec and see a few examples.
Also, it's Go and not GoLang (Read). Always prefer the former over the latter.
Try the Go Tour. Some concepts are explained well.
Also, read Go's FAQ and Pike's blog on declaration syntax. The FAQ should answer many such queries.
import "io"
Go has fmt and io packages, although they do have some overlap. For example, fmt.Fprint lets you write to any io.Writer, and fmt.Fscan lets you read from any io.Reader.
Similarly you can write to console with io.Copy(os.Stdout, something), and read from console with io.Copy(something, os.Stdin).
int sum(int a, int b) {
I think I read that by having func first, it makes lexical parsing much faster. Also Go function can have named return values:
func sum(a int, b int) (n int)
I am not sure how you'd do that with the other syntax.
int numbers[4] = {1, 2, 3, 4}
Go syntax allows you to omit the type, which you can't do with C.
foreach (n in range(numbers))
Go doesn't have a while keyword, for the reason that less keywords again makes for faster lexical parsing. Instead you have different for invocations:
var n int
for {
if n > 9 {
break
}
println(n)
n++
}
var n int
for n < 9 {
println(n)
n++
}
for n := 0; n < 9; n++ {
println(n)
}
for range numbers {
println("hello")
}
for index := range numbers {
println(index)
}
for index, value := range numbers {
println(index, value)
}
For this:
result = sum(n, 2)
Go has two different syntax for variable assignment:
result := 1
result = 2
First is a declaration, second is assigning to an already declared variable.
io.print(result)
fmt.Println is uppercase, because any function that starts with an uppercase letter is a "public" function. This saves on typing public or pub everywhere.
trying to check if a given number is less than the other or not, so wrote the below:
package main
import "fmt"
type T interface {}
func Less(i, j T) bool {
return i > j
}
But got the below error:
# command-line-arguments
.\hashmap.go:23:11: invalid operation: i > j (operator > not defined on interface)
How can I add such mathematical operations to general type element (number or string)?
interface is not a type, nor is it a generic value representing intersection of all types like string and int. When you ask for whether a is greater than or equal to b, you'll have to also specify which exact types those two belong (implicitly, they should have the same type), to answer that question. Similarly, Go also needs that information. Doing that for interface can be come a little cumbersome. However, there are a couple of ways:
Define the Less function individually on both types:
func LessString(n1, n2 string) bool {
return strings.Compare(n1, n2) == -1
}
// no need to define a less function, but whatever
func LessInt(n1, n2 int) bool {
return n1 < n2
}
Or, using type aliases:
type Name string
type Age int
func (name Name) Less(compare string) bool {
return strings.Compare(string(name), compare) == -1
}
func (age Age) LessInt(compare int) bool {
return int(age) < compare
}
A second way to implement Less functionality on interface would be to do type assertions:
// This isn't all that useful, but whatever
type T interface{}
func main() {
fmt.Println(Less(10, 20))
fmt.Println(Less("10", "20"))
}
func Less(t1, t2 T) bool {
switch v1 := t1.(type) {
case string:
return strings.Compare(v1, t2.(string)) < 0 // import "strings"
// return v1 < t2.(string)
case int:
return v1 < t2.(int)
}
return false
}
There is no way in Go to define operators over types. It prefers adding functions to achieve that. Many standard library modules follow similar patterns.
Why do we have the methods declared outside the type definition of the struct? E.g.:
type antenna struct {
name string
length float32
girth float32
bloodtype string
}
func (p *antenna) extend() {
p.length += 10
}
It seems to me that the method could be part of the struct? (Let's ignore for now that structs are supposed to be value types)
type antenna struct {
name string
length float32
girth float32
bloodtype string
func extend() {
length += 10
}
}
This would be more similar to traditional OOP. I didn't find any good explanations of why it is done the way it is besides "structs are value-types and classes are reference-types". I know the difference, but it's not a satisfactory answer to me. In any way the method has to be called like this:
var x = antenna()
x.extend()
So what's the point of separating the the struct and methods? Having them visually grouped together in the code - as in typical OOP languages - seems useful to me?
TLR: Code reuse, and Consistency.
1 - This enables to reuse methods:
This is the key design principle of the interface type in Go - let me make it more clear with an example: Consider you need to sort an slice of int (try it here):
a := []int{1, 3, 2, 5, 4}
sort.Ints(a) // sort.Sort(sort.IntSlice(a))
fmt.Println(a) // [1 2 3 4 5]
You simply call sort.Ints(a) which then calls Sort(IntSlice(a)) inside the standard library:
type IntSlice []int
func (x IntSlice) Len() int { return len(x) }
func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
sort.IntSlice attaches the 3 methods of sort.Interface: Len, Less, and Swap to the type []int, to call:
// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
quickSort(data, 0, n, maxDepth(n))
}
So you are able to reuse methods from the standard library, and you don't need to reimplement it again.
2- You may define your own types, See this example - There is no inside here for this named type - so methods must be outside of this type:
package main
import "fmt"
type num int32
func (p *num) inc() {
*p++
}
func main() {
p := num(100)
p.inc()
fmt.Println(p) // 101
}
The above named type num versus this user defined type: By design this makes the Go language consistent for both types:
type Animal struct {
Name string
moves []move.Direction
}
func (p *Animal) Walk(dir move.Direction) {
p.moves = append(p.moves, dir)
}
See also:
In Go is naming the receiver variable 'self' misleading or good practice?
In C/C++ (and many languages of that family), a common idiom to declare and initialize a variable depending on a condition uses the ternary conditional operator :
int index = val > 0 ? val : -val
Go doesn't have the conditional operator. What is the most idiomatic way to implement the same piece of code as above ? I came to the following solution, but it seems quite verbose
var index int
if val > 0 {
index = val
} else {
index = -val
}
Is there something better ?
As pointed out (and hopefully unsurprisingly), using if+else is indeed the idiomatic way to do conditionals in Go.
In addition to the full blown var+if+else block of code, though, this spelling is also used often:
index := val
if val <= 0 {
index = -val
}
and if you have a block of code that is repetitive enough, such as the equivalent of int value = a <= b ? a : b, you can create a function to hold it:
func min(a, b int) int {
if a <= b {
return a
}
return b
}
...
value := min(a, b)
The compiler will inline such simple functions, so it's fast, more clear, and shorter.
No Go doesn't have a ternary operator. Using if/else syntax is the idiomatic way.
Why does Go not have the ?: operator?
There is no ternary testing operation in Go. You may use the following to achieve the same result:
if expr {
n = trueVal
} else {
n = falseVal
}
The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.
— Frequently Asked Questions (FAQ) - The Go Programming Language
Suppose you have the following ternary expression (in C):
int a = test ? 1 : 2;
The idiomatic approach in Go would be to simply use an if block:
var a int
if test {
a = 1
} else {
a = 2
}
However, that might not fit your requirements. In my case, I needed an inline expression for a code generation template.
I used an immediately evaluated anonymous function:
a := func() int { if test { return 1 } else { return 2 } }()
This ensures that both branches are not evaluated as well.
The map ternary is easy to read without parentheses:
c := map[bool]int{true: 1, false: 0} [5 > 4]
Foreword: Without arguing that if else is the way to go, we can still play with and find pleasure in language-enabled constructs.
Go 1.18 generics update: Go 1.18 adds generics support. It is now possible to create a generic If() function like this. Note: This is available in github.com/icza/gog, as gog.If() (disclosure: I'm the author).
func If[T any](cond bool, vtrue, vfalse T) T {
if cond {
return vtrue
}
return vfalse
}
Which you can use like this:
min := If(i > 0, i, 0)
The pre-1.18 answer follows:
The following If construct is available in my github.com/icza/gox library with lots of other methods, being the gox.If type.
Go allows to attach methods to any user-defined types, including primitive types such as bool. We can create a custom type having bool as its underlying type, and then with a simple type conversion on the condition, we have access to its methods. Methods that receive and select from the operands.
Something like this:
type If bool
func (c If) Int(a, b int) int {
if c {
return a
}
return b
}
How can we use it?
i := If(condition).Int(val1, val2) // Short variable declaration, i is of type int
|-----------| \
type conversion \---method call
For example a ternary doing max():
i := If(a > b).Int(a, b)
A ternary doing abs():
i := If(a >= 0).Int(a, -a)
This looks cool, it's simple, elegant, and efficient (it's also eligible for inlining).
One downside compared to a "real" ternary operator: it always evaluates all operands.
To achieve deferred and only-if-needed evaluation, the only option is to use functions (either declared functions or methods, or function literals), which are only called when / if needed:
func (c If) Fint(fa, fb func() int) int {
if c {
return fa()
}
return fb()
}
Using it: Let's assume we have these functions to calculate a and b:
func calca() int { return 3 }
func calcb() int { return 4 }
Then:
i := If(someCondition).Fint(calca, calcb)
For example, the condition being current year > 2020:
i := If(time.Now().Year() > 2020).Fint(calca, calcb)
If we want to use function literals:
i := If(time.Now().Year() > 2020).Fint(
func() int { return 3 },
func() int { return 4 },
)
Final note: if you would have functions with different signatures, you could not use them here. In that case you may use a function literal with matching signature to make them still applicable.
For example if calca() and calcb() would have parameters too (besides the return value):
func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }
This is how you could use them:
i := If(time.Now().Year() > 2020).Fint(
func() int { return calca2(0) },
func() int { return calcb2(0) },
)
Try these examples on the Go Playground.
func Ternary(statement bool, a, b interface{}) interface{} {
if statement {
return a
}
return b
}
func Abs(n int) int {
return Ternary(n >= 0, n, -n).(int)
}
This will not outperform if/else and requires cast but works. FYI:
BenchmarkAbsTernary-8 100000000 18.8 ns/op
BenchmarkAbsIfElse-8 2000000000 0.27 ns/op
If all your branches make side-effects or are computationally expensive the following would a semantically-preserving refactoring:
index := func() int {
if val > 0 {
return printPositiveAndReturn(val)
} else {
return slowlyReturn(-val) // or slowlyNegate(val)
}
}(); # exactly one branch will be evaluated
with normally no overhead (inlined) and, most importantly, without cluttering your namespace with a helper functions that are only used once (which hampers readability and maintenance). Live Example
Note if you were to naively apply Gustavo's approach:
index := printPositiveAndReturn(val);
if val <= 0 {
index = slowlyReturn(-val); // or slowlyNegate(val)
}
you'd get a program with a different behavior; in case val <= 0 program would print a non-positive value while it should not! (Analogously, if you reversed the branches, you would introduce overhead by calling a slow function unnecessarily.)
As others have noted, golang does not have a ternary operator or any equivalent. This is a deliberate decision thought to improve readability.
This recently lead me to a scenario where constructing a bit-mask in a very efficient manner became hard to read when written idiomatically, or very inefficient when encapsulated as a function, or both, as the code produces branches:
package lib
func maskIfTrue(mask uint64, predicate bool) uint64 {
if predicate {
return mask
}
return 0
}
producing:
text "".maskIfTrue(SB), NOSPLIT|ABIInternal, $0-24
funcdata $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
funcdata $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
movblzx "".predicate+16(SP), AX
testb AL, AL
jeq maskIfTrue_pc20
movq "".mask+8(SP), AX
movq AX, "".~r2+24(SP)
ret
maskIfTrue_pc20:
movq $0, "".~r2+24(SP)
ret
What I learned from this was to leverage a little more Go; using a named result in the function (result int) saves me a line declaring it in the function (and you can do the same with captures), but the compiler also recognizes this idiom (only assign a value IF) and replaces it - if possible - with a conditional instruction.
func zeroOrOne(predicate bool) (result int) {
if predicate {
result = 1
}
return
}
producing a branch-free result:
movblzx "".predicate+8(SP), AX
movq AX, "".result+16(SP)
ret
which go then freely inlines.
package lib
func zeroOrOne(predicate bool) (result int) {
if predicate {
result = 1
}
return
}
type Vendor1 struct {
Property1 int
Property2 float32
Property3 bool
}
// Vendor2 bit positions.
const (
Property1Bit = 2
Property2Bit = 3
Property3Bit = 5
)
func Convert1To2(v1 Vendor1) (result int) {
result |= zeroOrOne(v1.Property1 == 1) << Property1Bit
result |= zeroOrOne(v1.Property2 < 0.0) << Property2Bit
result |= zeroOrOne(v1.Property3) << Property3Bit
return
}
produces https://go.godbolt.org/z/eKbK17
movq "".v1+8(SP), AX
cmpq AX, $1
seteq AL
xorps X0, X0
movss "".v1+16(SP), X1
ucomiss X1, X0
sethi CL
movblzx AL, AX
shlq $2, AX
movblzx CL, CX
shlq $3, CX
orq CX, AX
movblzx "".v1+20(SP), CX
shlq $5, CX
orq AX, CX
movq CX, "".result+24(SP)
ret
eold's answer is interesting and creative, perhaps even clever.
However, it would be recommended to instead do:
var index int
if val > 0 {
index = printPositiveAndReturn(val)
} else {
index = slowlyReturn(-val) // or slowlyNegate(val)
}
Yes, they both compile down to essentially the same assembly, however this code is much more legible than calling an anonymous function just to return a value that could have been written to the variable in the first place.
Basically, simple and clear code is better than creative code.
Additionally, any code using a map literal is not a good idea, because maps are not lightweight at all in Go. Since Go 1.3, random iteration order for small maps is guaranteed, and to enforce this, it's gotten quite a bit less efficient memory-wise for small maps.
As a result, making and removing numerous small maps is both space-consuming and time-consuming. I had a piece of code that used a small map (two or three keys, are likely, but common use case was only one entry) But the code was dog slow. We're talking at least 3 orders of magnitude slower than the same code rewritten to use a dual slice key[index]=>data[index] map. And likely was more. As some operations that were previously taking a couple of minutes to run, started completing in milliseconds.\
One-liners, though shunned by the creators, have their place.
This one solves the lazy evaluation problem by letting you, optionally, pass functions to be evaluated if necessary:
func FullTernary(e bool, a, b interface{}) interface{} {
if e {
if reflect.TypeOf(a).Kind() == reflect.Func {
return a.(func() interface{})()
}
return a
}
if reflect.TypeOf(b).Kind() == reflect.Func {
return b.(func() interface{})()
}
return b
}
func demo() {
a := "hello"
b := func() interface{} { return a + " world" }
c := func() interface{} { return func() string { return "bye" } }
fmt.Println(FullTernary(true, a, b).(string)) // cast shown, but not required
fmt.Println(FullTernary(false, a, b))
fmt.Println(FullTernary(true, b, a))
fmt.Println(FullTernary(false, b, a))
fmt.Println(FullTernary(true, c, nil).(func() string)())
}
Output
hello
hello world
hello world
hello
bye
Functions passed in must return an interface{} to satisfy the internal cast operation.
Depending on the context, you might choose to cast the output to a specific type.
If you wanted to return a function from this, you would need to wrap it as shown with c.
The standalone solution here is also nice, but could be less clear for some uses.
Now with the release of go1.18 generics, it's very easy to do it with a generic function like this, and it is reusable through your whole app
package main
import (
"fmt"
)
func Ternary[T any](condition bool, If, Else T) T {
if condition {
return If
}
return Else
}
func main() {
fmt.Println(Ternary(1 < 2, "yes", "no")) // yes
fmt.Println(Ternary(1 < 2, 1, 0)) // 1
fmt.Println(Ternary[bool](1 < 2, true, false)) // true
}
be aware if you use it in this case it will crash.
in this case, just use an if statement,
(because you passing into the function a nil pointer VS an if statement is not calling that section if it is false)
var a *string
fmt.Println(Ternary(a != nil, *a, "some thing else"))
the solution call it with a function, so it will not be excuted if it's false
func TernaryPointer[T any](condition bool, If, Else func() T) T {
if condition {
return If()
}
return Else()
}
var pString *string
fmt.Println(TernaryPointer(
pString != nil, // condition
func() string { return *pString }, // true
func() string { return "new data" }, // false
))
but in this case, I think a regular if statement is cleaner (except if go adds arrow functions in the future)
playground
give credit for this answer he already answered it
I have compiled some items and compared the speed.
/*
go test ternary_op_test.go -v -bench="^BenchmarkTernaryOperator" -run=none -benchmem
*/
package _test
import (
"testing"
)
func BenchmarkTernaryOperatorIfElse(b *testing.B) {
for i := 0; i < b.N; i++ {
if i%2 == 0 {
_ = i
} else {
_ = -i
}
}
}
// https://stackoverflow.com/a/45886594/9935654
func Ternary(statement bool, a, b interface{}) interface{} {
if statement {
return a
}
return b
}
func BenchmarkTernaryOperatorTernaryFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Ternary(i%2 == 0, i, -i).(int)
}
}
// https://stackoverflow.com/a/34636594/9935654
func BenchmarkTernaryOperatorWithFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = func() int {
if i%2 == 0 {
return i
} else {
return -i
}
}
}
}
// https://stackoverflow.com/a/31483763/9935654
func BenchmarkTernaryOperatorMap(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = map[bool]int{true: i, false: -i}[i%2 == 0]
}
}
output
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i7-8565U CPU # 1.80GHz
BenchmarkTernaryOperatorIfElse
BenchmarkTernaryOperatorIfElse-8 1000000000 0.4460 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorTernaryFunc
BenchmarkTernaryOperatorTernaryFunc-8 1000000000 0.3602 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorWithFunc
BenchmarkTernaryOperatorWithFunc-8 659517496 1.642 ns/op 0 B/op 0 allocs/op
BenchmarkTernaryOperatorMap
BenchmarkTernaryOperatorMap-8 13429532 82.48 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 4.365s
One more suggestion for the idiomatic approach in Go of ternary operator:
package main
import (
"fmt"
)
func main() {
val := -5
index := func (test bool, n, d int) int {
if test {
return n
}
return d
}(val > 0, val, -val)
fmt.Println(index)
}
Go Playground
I was playing with a solution that doesn't use the three arguments function.
Don't take me wrong, the three arguments solution works great but personally i like to name things explicitly.
What i'd love is an explicit interface like that:
When(<condition>).Then(<true value>).Else(<false value>)
I implemented this like that:
type Else[T any] interface {
ElseDo(fn func() T) T
Else(value T) T
}
type Then[T any] interface {
ThenDo(fn func() T) Else[T]
Then(value T) Else[T]
}
type Condition[T any] struct {
condition bool
thenValue T
thenFn func() T
}
func When[T any](condition bool) Then[T] {
return &Condition[T]{condition: condition}
}
func (c *Condition[T]) ThenDo(fn func() T) Else[T] {
c.thenFn = fn
return c
}
func (c *Condition[T]) Then(value T) Else[T] {
c.thenValue = value
return c
}
func (c *Condition[T]) ElseDo(fn func() T) T {
if c.condition {
return c.then()
}
return fn()
}
func (c *Condition[T]) Else(value T) T {
if c.condition {
return c.then()
}
return value
}
func (c *Condition[T]) then() T {
if c.thenFn != nil {
return c.thenFn()
}
return c.thenValue
}
Usage:
When[int](something == "expectedValue").Then(0).Else(1)
When[int](value > 0).Then(value).Else(1)
When[int](value > 0).ThenDo(func()int {return value * 4}).Else(1)
When[string](boolean == true).Then("it is true").Else("it is false")
Unfortunately i didn't find a way to get rid of the explicit type when calling the When function. The type is not automatically inferred by the return types of Then/Else 🤷♂️