Idiomatic way to constant namespaces?

1,521 views
Skip to first unread message

Maxim Kouprianov

unread,
Jun 26, 2014, 8:59:05 PM6/26/14
to golan...@googlegroups.com
Hi all. I'm thinking about organization of constants in my code, for example I have a category "Reply" and the straightforward way to make a set of related constants is 
const (
ReplyOk                = "OK"
ReplyRing              = "RING"
ReplyError             = "ERROR"
ReplyNotSupported      = "COMMAND NOT SUPPORT"
ReplyNoCarrier         = "NO CARRIER"
ReplyTooManyParameters = "TOO MANY PARAMETERS"
ReplyNoAnswer          = "NO ANSWER"
)

But that is too repetitive and spams my code-completion box. So I've decided to organize it this way:
var Reply = struct {
Ok                string
Ring              string
Error             string
NotSupported      string
NoCarrier         string
TooManyParameters string
NoAnswer          string
}{
"OK",
"RING",
"ERROR",
"COMMAND NOT SUPPORT",
"NO CARRIER",
"TOO MANY PARAMETERS",
"NO ANSWER",
}

It's not constant anymore, but I now have a namespace, i.e.
if r == Reply.Ok
 and so on, this reminds me of C#.

The question is: is there any serious drawbacks of this approach, if used with a large set of namespaces? Is it any alternative solution that will help to deal with repetitiveness?

-- Thanks.

Konstantin Khomoutov

unread,
Jun 27, 2014, 6:36:45 AM6/27/14
to Maxim Kouprianov, golan...@googlegroups.com
Go does not have namespaces so it's best to not touch this word at all.
And, no, when it comes to names, the dot in Go is only used to access
members of compound data types, methods and symbols in imported modules.
So the only way to get "Reply.Ok" without using an object named "Reply"
is to have a module named Reply which contains a symbol Ok.

Having said that, I think that what you're trying to combat with is
really a non-issue. I like C#'s approach to enums as you do, but Go is
not C# so as with any language, the best advise is to do what's
accepted by the community, so I'd use ReplyOk et al. For instance, see
what constants the net/http standard module [1] exports.

On the other hand, making constants out of strings may be a dubious
practice because Go is not C and I'd not be surprised that for the
program

const a = "foo"
...
var b = a

the compiler would generate the code literally copying the bytes of
"foo" into a new string value created for b. So may be your approach
of using a struct is just Ok. Also note that the usual way of having
simple exported errors in a module is to create exported variables which
are essentially string-valued Error interfaces (see [1] again -- this
time for exported variables).

1. http://golang.org/pkg/net/http/

Nate Finch

unread,
Jun 27, 2014, 7:56:01 AM6/27/14
to golan...@googlegroups.com, m...@kc.vc
I think what you want is this:


package reply

type Type int

const (
Ok = iota
Ring
Error
NotSupported
NoCarrier
TooManyParameters
NoAnswer
)

func (t Type) String() string {
switch t {
case Ok:
return "OK"
case Ring:
return "RING"
case Error:
return "ERROR"
case NotSupported:
return "COMMAND NOT SUPPORT"
case NoCarrier:
return "NO CARRIER"
case TooManyParameters:
return "TOO MANY PARAMETERS"
case NoAnswer:
return "NO ANSWER"
}
return "ERROR"
}


// elsewhere:


// ...

if repl == reply.Ok {
}



Nate Finch

unread,
Jun 27, 2014, 7:57:07 AM6/27/14
to golan...@googlegroups.com, m...@kc.vc
ack, important typo....  that should be:

const (
     Ok Type = iota

On Friday, June 27, 2014 7:56:01 AM UTC-4, Nate Finch wrote:
I think what you want is this:


package reply

type Type int

const (
Ok Type = iota

Maxim Kouprianov

unread,
Jun 27, 2014, 9:35:51 AM6/27/14
to golan...@googlegroups.com, m...@kc.vc
Thank you for the note about explicit copying,  I'll check that soon.
Actually, I have a lot of these enums, also they're in sub package already, so I don't want to have a package hell in my source tree. :)
In your variant you've doubled every name "NotSupported", "TooManyParameters", ... I have 50+ declarations and this will force me to do 100? Not good.

And I'll never choose "COMMAND NOT SUPPORT" for the String() output, in my case these strings were intended for the parsing, like
`expect(Sep + Reply.NotSupported + Sep)` or an external use like `at.expect(at.Sep + at.Reply.NotSupported + at.Sep)`, where at.Sep = "\r\n".
This seems to be very nice... And as long as there is no any outstanding alternative, I'll stick with it.


пятница, 27 июня 2014 г., 15:56:01 UTC+4 пользователь Nate Finch написал:

Nate Finch

unread,
Jun 27, 2014, 1:05:55 PM6/27/14
to golan...@googlegroups.com, m...@kc.vc
So, I only made it its own package because you really wanted reply.Ok to work.  If it were me, I'd just make it OkReply (so the repetitive part is at the end), and put it in whatever package.  But it's up to you.

For your solution, I suggest making the following tweak:

type ReplyType string

var Reply = struct {
Ok                ReplyType
Ring              ReplyType
Error             ReplyType
NotSupported      ReplyType
NoCarrier         ReplyType
TooManyParameters ReplyType
NoAnswer          ReplyType
}{
"OK",
"RING",
"ERROR",
"COMMAND NOT SUPPORT",
"NO CARRIER",
"TOO MANY PARAMETERS",
"NO ANSWER",
}

This way, it prints like a string, but if something is expecting a ReplyType, and you pass it some random string by accident, it won't compile (and vice versa), which may save you some headaches.

Maxim Kouprianov

unread,
Jun 27, 2014, 2:30:23 PM6/27/14
to golan...@googlegroups.com, m...@kc.vc
I suggest making the following tweak
Agreed, it's also much easier to have values with annotations now

type Opt struct {
Id          int
Description string
 
var ServiceState = struct {
None               Opt
Restricted         Opt
Valid              Opt
RestrictedRegional Opt
PowerSaving        Opt
}{
Opt{0, "No service"},
Opt{1, "Restricted service"}, 
 ....
 

пятница, 27 июня 2014 г., 21:05:55 UTC+4 пользователь Nate Finch написал:
Reply all
Reply to author
Forward
0 new messages