Hacker News new | past | comments | ask | show | jobs | submit login

I've been using Go as my preferred language for almost a decade now. Hackish "clever" solutions like these coming from designers of the language like Rob Pike and Dace Cheney simply indicate that the language is hitting its limits of expressivity and needs to start thinking hard about overloading which is the right solution to problems like this.



Rob Pike and Dave Cheney know way better than me, but I can't help to think they're trying to be "easy rather than simple", here.

Rob doesn't explain why a config struct is not good for him in his reference article, and Dave is saying that's because 1/ it needs to be passed even when it's empty and 2/ its zero values may be a trouble, giving the example of explicitly setting `Port` to 0 to let OS selecting first free port available.

I would say that I have absolutely no problem with passing an empty option struct : that's simple. I know what is going on, I don't have to check sources for three methods to understand it.

Regarding the zero value problem, the example of port selection seems incredibly an edge case. Most of the time, if some option is numeric and can have a special feature, -1 will be used rather than 0 (like it's often the case when passing a limit to specify "no limit"). For the port problem, I would have no problem passing an `AutoselectFreePort bool` option.

But then again, maybe the pattern seems complicated to me because it's new. We'll see with time.


The zero value problem would be a non-issue if Go had a built-in ?T type ("option T").


That's problematic, thanks to golang choosing to have always-nullable reference types. "option T*" would therefore be a tristate type, unless it was somehow specialised into representing nullability (obviously a major breaking change).


If mostly everyone uses Option instead of `nil`, the problem approaches a vanishing point. Not perfect, but still a massive improvement. Of course, generics won't be added until Go 2 anyway, at which point the community is free to make a breaking change, though I doubt they'll ditch `nil`.


For the zero value problem, a pointer is usually used rather than an "identifier value" like -1.

var price *float64

if price == nil {

  // Handle empty value

}


This works if you can guarantee the variable will not ever change after being set which is not unreasonable for configuration options.

However if you are dealing with variables that can mutate in a concurrent environment, you might have a race condition if you pass by pointer reference rather than by value


I had the same feeling, and then i tried to implement the same solution in swift (which has more evolved constructs for enums and option sets), but it's actually not easy to do as well, if you want to handle all those constraints at the same time:

1/ a potentially big number of options, without creating functions with a huge list of parameters

2/ options with associated values (revision: int)

3/ reusable options between different operations, with type safety preventing wrong combination.

=> 1/ prevents you from using function overloading and default parameters

=> 2/ prevents you from using optionset (they're just bit masks)

and

=> 3/ prevents you from using regular sets or arrays as operation parameter.

I haven't spent more than half an hour trying to find a solution, so maybe there's a smart trick that would work, but it won't be an obvious solution either.


I realise that you tried to work with the same constraints as the Go solution for the sake of argument.

But the whole rationale for having 3/ in the first place isn't valid in Swift, so it seems to me that the obvious Swift solution is to use an enum per operation.


It does makes quite a bit of sense, especially in the case where all those enums end up being processed by one single private function, such as "configureServer(config: ConfOption) ". (i'm refering to the linked post by Dave cheney here)

I spent a bit more time on the problem, and now i'm pretty sure it is also not solvable "cleanly" in swift, because it actually would need to model some kind of constraints over the accepted values of a type for a parameter.

Something like get(opt: Options<restrictedTo [.prefix, .revision]>... )

Otherwise you'll need to manually create a subenum, and manually compare cases from the original one with the sub one.


Why do you find this hackish? I find that pretty straight forward given the constraints. You might prefer a more expressive language but that comes at a cost.


[flagged]


That's right. I've been using Go since pre 1.0 times. I'm however not by and large a programmer by profession.


> I'm however not by and large a programmer by profession.

That explains it. Different risk tolerances when you're not reliant on it for a living.

Apologies for whatever I was flagged for, if I came across as rude in text that was not my intention. Legitimate curiosity.


No worries. I didn't flag you actually :-)




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: