Other problems though such as default function parameters are less likely to be addressed (it seems to me). This is a problem because it means lots of code will be written in which there are implicit defaults (just pass 0!) that are not well-understood or guaranteed by the interface. The answer there is "just write another method with a different name and parameters". Ok so now I have to think of sensible names for the methods that are exactly the same as another method except they apply defaults...and write and maintain more code of course - especially if its in an interface you re-use.
It's unlikely to be addressed given that Google style guide forbids using default function parameters even in C++.
> This is a problem because it means lots of code will be written in which there are implicit defaults (just pass 0!)
"Just pass 0" is not a good way to implement defaults.
I exported this struct:
type Args struct {
Key []byte // secret key for MAC, KDF, or stream cipher
Person []byte // personalization string
PublicKey []byte // public key for signature hashing
KeyId []byte // key identifier for KDF
Nonce []byte // nonce for stream cipher or randomized hashing
}
and then provided a function: func New(outLen uint64, args *Args) *Hash
Users can easily call it like this: h := skein.New(64, &skein.Args{ Key: someKey, Nonce : someNonce })
I like this design a lot.Plus, with structs as arguments you can easily provide more than one default configurations. For example, if you want to personalize hash for different usages:
var FileHashConfig = &skein.Args{ Person: []byte("file hash for MyApp") }
var MessageHashConfig = &skein.Args{ Person: []byte("message hash for MyApp sync") }
and use them: h := skein.New(64, FileHashConfig)
(Note: Args is a bad name for general usage -- I called it like this only because Skein authors call these "arguments", and "configuration" is reserved for something else.)There are a few specified usages for Skein with arguments, so to avoid making users of my library read the Skein paper, I wrote "constructors" for them:
// NewMAC returns hash.Hash calculating Skein Message Authentication Code of the
// given length in bytes. A MAC is a cryptographic hash that uses a key to
// authenticate a message. The receiver verifies the hash by recomputing it
// using the same key.
func NewMAC(outLen uint64, key []byte) hash.Hash {
return hash.Hash(New(outLen, &Args{Key: key}))
}
// NewStream returns a cipher.Stream for encrypting a message with the given key
// and nonce. The same key-nonce combination must not be used to encrypt more
// than one message. There are no limits on the length of key or nonce.
func NewStream(key []byte, nonce []byte) cipher.Stream {
const streamOutLen = (1<<64 - 1) / 8 // 2^64 - 1 bits
h := New(streamOutLen, &Args{Key: key, Nonce: nonce})
return newOutputReader(h)
}
Simple.To me this is the whole issue. Of course if we have knowledge outside the method declaration about what values can be left 0, then we can pass the correct ones as needed. But the interface is ambiguous about when that is acceptable, and the implementation can change without changing the interface.
According to the mail lists I've read on the subject, the correct way to implement your algorithms in Go would be for each to be a separate method. And indeed that is the only way in Go to support a fully specified interface.
In Go, when you create a new variable, it's automatically set to the null/zero value. So in this case, they would be nil.
So not the same thing with default values at all.
With this struct interface you just implement "optional" values...
In skein package:
var DefaultHash = &Args{ ... } // the default args you want
Users will call it like this: skein.New(32, &skein.DefaultHash)
Another way, better for some cases, is have methods on arguments struct, like in base32 package from stdlib http://golang.org/pkg/encoding/base32/ base32.StdEncoding.DecodeToString(...)
base32.HexEncoding.DecodeToString(...)
In most cases (but now all), there are better ways to design API than stuff them with default arguments, which make functions behave unexpectedly if you forget something.But maybe we should talk about specific cases? What are the examples of functions which need to accept default arguments? Maybe I can try to convert them into something reasonable in Go?
Because they do not share your belief that Go has "fundamental issues and well understood design issues".
If they did then your argument reductio ad absurdum would make sense. However, they don't; it does not.
I think it's safe to say they are aware of the issue and consider lack of generics a negative (because it slows you down, duplicates code, adds type unsafety, etc.). They don't have a solution they like yet.
(1) them not believing that Go has design issues
for my argument to not make sense. It would need both (1) and
(2) them being right about said belief
for my argument to be invalid. And while (1) might hold, (2) is highly debatable.
And idiomatic Java code (Factory of proxies of singletons) is laughed at.
And not a lot of people are fond of idiomatic Ruby code, which tends to rely on "magic" (or "action at a distance").
Sometimes idiomatic is good, sometimes it's bad. Let's not cargo cult.
But, as Go advocates will tell you any Generics related discussion in the golang list: "just try programming in Go for a while and you'll find you don't need Generics".
Why do they assume that people complaining haven't used the language "enough", is beyond me.
Plus it seems they believe that there is some amount of time of using a language, after which fundamental issues and well understood design issues disappear...
But, by that logic, why abandon C in the first place?
"Just use C long enough, and you'll find out you don't need garbage collection, built-in collections, closures and memory safety".
Or:
"Just use assembly long enough, and you'll find out you don't need all those fancy functions, and variables, and looping constructs, and stuff".