how do you chain commands on several lines in go? - go

I want to chain commands this way :
var cmdGroups = []*commands.CmdGroup {
commands.MakeCmdGroup("foo", cmd1, cmd2, cmd3).AddConstraint(cmd1, cmd2).AddConstraint(cmd2, cmd1, cmd3),
commands.MakeCmdGroup("bar", cmd1, cmd4).AddConstraint(cmd1, cmd4),
}
I'd like to split my chains on several lines for 80-column-lengths reasons, but Go won't let me compile this :
var cmdGroups = []*commands.CmdGroup {
commands.MakeCmdGroup("foo", cmd1, cmd2, cmd3)
.AddConstraint(cmd1, cmd2)
.AddConstraint(cmd2, cmd1, cmd3),
commands.MakeCmdGroup("bar", cmd1, cmd4)
.AddConstraint(cmd1, cmd4),
}
what can I do ?

As FUZxxl pointed out, your problem is the automatic insertion of semicolons.
The spec says:
When the input is broken into tokens, a semicolon is automatically
inserted into the token stream at the end of a non-blank line if the
line's final token is
an identifier
an integer, floating-point, imaginary, rune, or string
literal
one of the keywords break, continue, fallthrough, or return
one of the operators and delimiters ++, --, ), ], or }
You have a function call, which counts for a ) so a semicolon is added at the end of the line.
To circumvent automatic semicolon conversion you can write your calls in one of the following
ways:
Use the . instead of semicolon:
x.
Method(p1,p2,p3)
Break after beginning of parameter list instead of before the function:
x.Method(
p1,p2,p3, // , at the end is important to prevent semicolon insertion
)
If you dislike the methods above, you can (as of go1.1) treat the methods as first class
citizens and temporarily create shortcuts which might be shorter:
f = x.Method
f(p1,p2,p3).f(p3,p4,p5)
I haven't thought enough
with this example. f(...).f(...) is of course not possible, as the return value of f has no member f.
One would have to reassign f. So you gain nothing from that.

I would probably write some variant of:
var cmdGroups = []*commands.CmdGroup{
commands.MakeCmdGroup(
"foo", cmd1, cmd2, cmd3,
).AddConstraint(
cmd1, cmd2,
).AddConstraint(
cmd2, cmd1, cmd3,
),
commands.MakeCmdGroup(
"bar", cmd1, cmd4,
).AddConstraint(cmd1, cmd4),
}
However, such long selector operator chains are not to be seen in idiomatic code too often. (I consider the standard library an informal guide to idiomatic code). Perhaps there might be some weakness in this code design/structure.

Related

Why is there a comma in this Golang struct creation?

I have a struct:
type nameSorter struct {
names []Name
by func(s1, s2 *Name) bool
Which is used in this method. What is going on with that comma? If I remove it there is a syntax error.
func (by By) Sort(names []Name) {
sorter := &nameSorter{
names: names,
by: by, //why does there have to be a comma here?
}
sort.Sort(sorter)
Also, the code below works perfectly fine and seems to be more clear.
func (by By) Sort(names []Name) {
sorter := &nameSorter{names, by}
sort.Sort(sorter)
For more context this code is part of a series of declarations for sorting of a custom type that looks like this:
By(lastNameSort).Sort(Names)
This is how go works, and go is strict with things like comma and parentheses.
The good thing about this notion is that when adding or deleting a line, it does not affect other line. Suppose the last comma can be omitted, if you want to add a field after it, you have to add the comma back.
See this post: https://dave.cheney.net/2014/10/04/that-trailing-comma.
From https://golang.org/doc/effective_go.html#semicolons:
the lexer uses a simple rule to insert semicolons automatically as it scans, so the input text is mostly free of them
In other words, the programmer is unburdened from using semicolons, but Go still uses them under the hood, prior to compilation.
Semicolons are inserted after the:
last token before a newline is an identifier (which includes words like int and float64), a basic literal such as a number or string constant, or one of the tokens break continue fallthrough return ++ -- ) }
Thus, without a comma, the lexer would insert a semicolon and cause a syntax error:
&nameSorter{
names: names,
by: by; // semicolon inserted after identifier, syntax error due to unclosed braces
}

TCL/TK script issue with string match inside if-statement

I have a script in bash that calls a TCL script for each element on my network which performs some actions based on the type of the element. This is part of the code that checks whether or not the hostname contains a specific pattern(e.g. *CGN01) and then gives the appropriate command to that machine.
if {[string match "{*CGN01}" $hostname] || $hostname == "AthMet1BG01"} {
expect {
"*#" {send "admin show inventory\r"; send "exit\r"; exp_continue}
eof
}
}
With the code i quoted above i get no error BUT when the hostname is "PhiMSC1CGN01" then the code inside the if is not executed which means that the expression is not correct.
I have tried everything (use of "()" or "{}" or"[]" inside the if) but when i dont put "" on the pattern i get an error like:
invalid bareword "string"
in expression "(string match {*DR0* *1TS0* *...";
should be "$string" or "{string}" or "string(...)" or ...
(parsing expression "(string match {*DR0* *...")
invoked from within
"if {$hostname == "AthMar1BG03" || [string match *CGN01 $hostname]...
or this:
expected boolean value but got "[string match -nocase "*CGN01" $hostname]==0"
while executing
"if {$hostname == "AthMar1BG03" || {[string match -nocase "*CGN01" $hostname]==0}...
when i tried to use ==0 or ==1 on the expression.
My TCL-Version is 8.3 and i cant update it because the machine has no internet connecticity :(
Please help me i am trying to fix this for over a month...
If you want to match a string that is either exactly AthMet1BG01 or any string that ends with CGN01, you should use
if {[string match *CGN01 $hostname] || $hostname == "AthMet1BG01"} {
(For Tcl 8.5 or later, use eq instead of ==.)
Some comments on your attempts:
(The notes about the expression language used by if go for expr and while as well. It is fully described in the documentation for expr.)
To invoke a command inside the condition and substitute its result, it needs to be enclosed in brackets ([ ]). Parentheses (( )) can be used to set the priority of subexpressions within the condition, but don't indicate a command substitution.
Normally, inside the condition strings need to be enclosed in double quotes or braces ({ }). This is because the expression language that is used to express the condition needs to distinguish between e.g. numbers and strings, which Tcl in general doesn't. Inside a command substitution within a condition, you don't need to use quotes or braces, as long as there are no characters in the string that you need to quote.
The string {abc} contains the characters abc. The string "{abc}" contains the characters {abc}, because the double quotes make the braces normal characters (the reverse also holds). [string match "{*bar}" $str] matches the string {foobar} (with the braces as part of the text), but not foobar.
If you put braces around a command substitution, {[incr foo]}, it becomes just the string [incr foo], i.e. the command isn't invoked and no substitution is made. If you use {[incr foo]==1} you get the string [incr foo]==1. The correct way to write this within an expression is [incr foo]==1, with optional whitespace around the ==.
All this is kind of hard to grok, but when you have it is really easy to use. Tcl is stubborn as a mule about interpreting strings, but carries heavy loads if you treat her right.
ETA an alternate matcher (see comments)
You can write your own alternate string matcher:
proc altmatch {patterns string} {
foreach pattern $patterns {
if {[string match $pattern $string]} {
return 1
}
}
return 0
}
If any of the patterns match, you get 1; if none of the patterns match, you get 0.
% altmatch {*bar f?o} foobar
1
% altmatch {*bar f?o} fao
1
% altmatch {*bar f?o} foa
0
For those who have a modern Tcl version, you can actually add it to the string ensemble so it works like other string commands. Put it in the right namespace:
proc ::tcl::string::altmatch {patterns string} {
... as before ...
and install it like this:
% set map [namespace ensemble configure string -map]
% dict set map altmatch ::tcl::string::altmatch
% namespace ensemble configure string -map $map
Documentation:
expr,
string,
Summary of Tcl language syntax
This command:
if {[string match "{*CGN01}" $hostname] || $hostname == "AthMet1BG01"} {
is syntactically valid but I really don't think that you want to use that pattern with string match. I'd guess that you really want:
if {[string match "*CGN01" $hostname] || $hostname == "AthMet1BG01"} {
The {braces} inside that pattern are not actually meaningful (string match only does a subset of the full capabilities of a glob match) so with your erroneous pattern you're actually trying to match a { at the start of $hostname, any number of characters, and then CGN01} at the end of $hostname. With the literal braces. Simply removing the braces lets PhiMSC1CGN01 match.

How to break a long line of code in Golang?

Coming from Python, I'm not used to see code lines longer than 80 columns.
So when I encounter this:
err := database.QueryRow("select * from users where user_id=?", id).Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)
I tried to break it to
err := database.QueryRow("select * from users where user_id=?", id) \
.Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)
But I get
syntax error: unexpected \
I also tried just breaking the line with hitting enter and put a semicolon at the end:
err := database.QueryRow("select * from users where user_id=?", id)
.Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email);
But the I again get:
syntax error: unexpected .
So I'm wondering what's the golangic way to do so?
First some background. The formal grammar of Go uses semicolons ";" as terminators in many productions, but Go programs may omit most of them (and they should to have a clearer, easily readable source; gofmt also removes unnecessary semicolons).
The specification lists the exact rules. Spec: Semicolons:
The formal grammar uses semicolons ";" as terminators in a number of productions. Go programs may omit most of these semicolons using the following two rules:
When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is
an identifier
an integer, floating-point, imaginary, rune, or string literal
one of the keywords break, continue, fallthrough, or return
one of the operators and delimiters ++, --, ), ], or }
To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".
So as you can see if you insert a newline character after the parenthesis ), a semicolon ; will be inserted automatically and so the next line will not be treated as the continuation of the previous line. This is what happened in your case, and so the next line starting with .Scan(&ReadUser.ID,... will give you a compile-time error as this standing by itself (without the previous line) is a compile-time error: syntax error: unexpected .
So you may break your line at any point which does not conflict with the rules listed under point 1. above.
Typically you can break your lines after comma ,, after opening parenthesis e.g. (, [, {, and after a dot . which may be referencing a field or method of some value. You can also break your line after binary operators (those that require 2 operands), e.g.:
i := 1 +
2
fmt.Println(i) // Prints 3
One thing worth noting here is that if you have a struct or slice or map literal listing the initial values, and you want to break line after listing the last value, you have to put a mandatory comma , even though this is the last value and no more will follow, e.g.:
s := []int {
1, 2, 3,
4, 5, 6, // Note it ends with a comma
}
This is to conform with the semicolon rules, and also so that you can rearrange and add new lines without having to take care of adding / removing the final comma; e.g. you can simply swap the 2 lines without having to remove and to add a new comma:
s := []int {
4, 5, 6,
1, 2, 3,
}
The same applies when listing arguments to a function call:
fmt.Println("first",
"second",
"third", // Note it ends with a comma
)
The simplest way is to simply leave the operator (.) on the first line.
\ line continuations are also discouraged in many python style guides, you could wrap the whole expression in parens if you are moving back and forth between go and python as this technique works in both languages.
As mentioned, this is a matter of style preference. I understand that the creators of Go have suggested a style based on their experience of which I learn from but also keep some of my own style from my experience.
Below is how I would format this:
err := database.
QueryRow("select * from users where user_id=?", id).
Scan(
&ReadUser.ID,
&ReadUser.Name,
&ReadUser.First,
&ReadUser.Last,
&ReadUser.Email,
)
It's a matter of style, but I like:
err := database.QueryRow(
"select * from users where user_id=?", id,
).Scan(
&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email,
)
what's the golangic way to do so?
An automated solution. Unfortunately, gofmt doesn't cover this case so you could use
https://github.com/segmentio/golines
Install it via
go install github.com/segmentio/golines#latest
Then run
golines -w -m 80 .
-w means make the changes in-place (default prints to stdout)
-m is max column length
You can break the line at several places like commas or braces as suggested by other answers. But Go community has this opinion on line length:
There is no fixed line length for Go source code. If a line feels too long, it should be refactored instead of broken.
There are several guidelines there in the styling guide. I am adding some of the notable ones (clipped):
Commentary
Ensure that commentary is readable from source even on narrow screens.
...
When possible, aim for comments that will read well on an 80-column wide terminal, however this is not a hard cut-off; there is no fixed line length limit for comments in Go.
Indentation confusion
Avoid introducing a line break if it would align the rest of the line with an indented code block. If this is unavoidable, leave a space to separate the code in the block from the wrapped line.
// Bad:
if longCondition1 && longCondition2 &&
// Conditions 3 and 4 have the same indentation as the code within the if.
longCondition3 && longCondition4 {
log.Info("all conditions met")
}
Function formatting
The signature of a function or method declaration should remain on a single line to avoid indentation confusion.
Function argument lists can make some of the longest lines in a Go source file. However, they precede a change in indentation, and therefore it is difficult to break the line in a way that does not make subsequent lines look like part of the function body in a confusing way:
// Bad:
func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string,
foo4, foo5, foo6 int) {
foo7 := bar(foo1)
// ...
}
// Good:
good := foo.Call(long, CallOptions{
Names: list,
Of: of,
The: parameters,
Func: all,
Args: on,
Now: separate,
Visible: lines,
})
// Bad:
bad := foo.Call(
long,
list,
of,
parameters,
all,
on,
separate,
lines,
)
Lines can often be shortened by factoring out local variables.
// Good:
local := helper(some, parameters, here)
good := foo.Call(list, of, parameters, local)
Similarly, function and method calls should not be separated based solely on line length.
// Good:
good := foo.Call(long, list, of, parameters, all, on, one, line)
// Bad:
bad := foo.Call(long, list, of, parameters,
with, arbitrary, line, breaks)
Conditionals and loops
An if statement should not be line broken; multi-line if clauses can lead to indentation confusion.
// Bad:
// The second if statement is aligned with the code within the if block, causing
// indentation confusion.
if db.CurrentStatusIs(db.InTransaction) &&
db.ValuesEqual(db.TransactionKey(), row.Key()) {
return db.Errorf(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
}
If the short-circuit behavior is not required, the boolean operands can be extracted directly:
// Good:
inTransaction := db.CurrentStatusIs(db.InTransaction)
keysMatch := db.ValuesEqual(db.TransactionKey(), row.Key())
if inTransaction && keysMatch {
return db.Error(db.TransactionError, "query failed: row (%v): key does not match transaction key", row)
}
There may also be other locals that can be extracted, especially if the conditional is already repetitive:
// Good:
uid := user.GetUniqueUserID()
if db.UserIsAdmin(uid) || db.UserHasPermission(uid, perms.ViewServerConfig) || db.UserHasPermission(uid, perms.CreateGroup) {
// ...
}
// Bad:
if db.UserIsAdmin(user.GetUniqueUserID()) || db.UserHasPermission(user.GetUniqueUserID(), perms.ViewServerConfig) || db.UserHasPermission(user.GetUniqueUserID(), perms.CreateGroup) {
// ...
}
switch and case statements should also remain on a single line.
// Good:
switch good := db.TransactionStatus(); good {
case db.TransactionStarting, db.TransactionActive, db.TransactionWaiting:
// ...
case db.TransactionCommitted, db.NoTransaction:
// ...
default:
// ...
}
// Bad:
switch bad := db.TransactionStatus(); bad {
case db.TransactionStarting,
db.TransactionActive,
db.TransactionWaiting:
// ...
case db.TransactionCommitted,
db.NoTransaction:
// ...
default:
// ...
}
If the line is excessively long, indent all cases and separate them with a blank line to avoid indentation confusion:
// Good:
switch db.TransactionStatus() {
case
db.TransactionStarting,
db.TransactionActive,
db.TransactionWaiting,
db.TransactionCommitted:
// ...
case db.NoTransaction:
// ...
default:
// ...
}
Never brake long URLs into multiple lines.
I have only added some of the few examples there are in the styling guide. Please read the guide to get more information.

Why and when should a comma be used at the end of a block?

There many cases in Rust when a block of code can end with or without comma.
For example:
enum WithoutComma
{
x,
y
}
or
enum WithComma
{
x,
y,
}
There are also other examples with match, etc. It seems that both variants lead to the same result. The only case I know where adding or removing a comma changes behaviour is the 1-element tuple declaration (which isn't a block):
let just_int = (5);
let tuple = (5,);
Why can one use a comma or not at the end of a block? Why is there such dualism in thelanguage and what are the reasons for it?
As you say, the only time a trailing comma is required is the 1-tuple pattern, type and construction let (x,): (Type,) = (1,). Everywhere else, trailing commas are optional, have no effect, but are allowed for a few reasons:
it makes macros easier: no need to be careful to not insert a comma at the very end of a sequence of items.
it makes diffs nicer when extending a list of things, e.g. adding a variant to
enum Foo {
Bar
}
gives
enum Foo {
Bar,
Baz
}
which is changing two lines (i.e. tools like git will display the Bar line as modified, as well as the inserted line), even though only the second actually had anything interesting in the change. If Bar started out with a trailing comma, then inserting Baz, after it is fine, with only one line changed.
They're not required (other than the 1-tuple) because that would be fairly strange (IMO), e.g.
fn foo(x: u16,) -> (u8, u8,) {
(bar(x,), baz(x,),)
}
(I guess it would look less strange for enum/struct declarations, but still, it's nice to be able to omit it.)

Why do programmers put spaces inside braces?

In my experience, it's common to see spaces put inside braces for one-line definitions, e.g. this function in JavaScript:
function(a, b) { return a * b; }
Is there any technical/historical reason that most programmers seem to do this, particularly given that spaces are not included inside parentheses?
Besides readability, in some languages, such as Verilog, identifiers can be escaped (by a \ at their beginning) so that they use special characters in their names. For example, the following names are legal identifier names in Verilog:
q
\q~ //escaped version which uses ~ in the name
\element[32] //a single variable (not part of an array) whose name is \element[32]
Such identifiers, should always terminate by space, otherwise the character after them would be considered as the identifier's name:
{ d, \q~ } // Concatenating d and \q~ in a vector
{ d, \q~} // Concatenating d and \q~} in a vector. Will generate a missing brace error.
Spaces are mostly used for readability. Most of the coding styles will tell you to judiciously use whitespace in your code to make it more readable.
As an example, look at this statement: *a = *b + *c;. Think how it will seem without the whitespace.

Resources