News for hackers. Stuff that matters.
Re:Google's Go Programming Language Grows Up: Now What? by dchest on Thursday September 20, 2012 @12:50PM (#4549444)
Today I was writing a Skein hash function implementation in Go. Skein can be configured for different purposes (e.g. MAC, KDF, stream cipher, etc.), and it can accept >5 optional arguments. I wanted to expose all these configuration option to users, in addition to providing a simple version that will create hash without configuration. Here's how I solved it (there's prior art in some standard libraries).

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.
Re:Google's Go Programming Language Grows Up: Now What? by jeremyjh on Thursday September 20, 2012 @09:52PM (#4551811)
So depending on the algorithm you are using, you are going to leave some of these fields in the struct as null/empty?

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.

Re:Google's Go Programming Language Grows Up: Now What? by earthboundkid on Thursday September 20, 2012 @11:42PM (#4552087)
> So depending on the algorithm you are using, you are going to leave some of these fields in the struct as null/empty?

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.

Re:Google's Go Programming Language Grows Up: Now What? by batista on Friday September 21, 2012 @04:15AM (#4552826)
Which is not necessarily the default value you want for them.

So not the same thing with default values at all.

With this struct interface you just implement "optional" values...

Re:Google's Go Programming Language Grows Up: Now What? by dchest on Friday September 21, 2012 @05:12AM (#4552934)
For default values, there are at least two choices. The first one I mentioned -- just create a variable and export it. Users can pass it:

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?

Re:Google's Go Programming Language Grows Up: Now What? by pacala on Friday September 21, 2012 @10:21AM (#4553836)
That is awful for anyone attempting to re-implement your API. It is totally unclear what field combinations are valid, and in time the situation is only going to get worse.
Re:Google's Go Programming Language Grows Up: Now What? by dchest on Saturday September 22, 2012 @03:38AM (#4557369)
There are no invalid combinations of fields in this case, any combination is valid. If there were invalid combinations, of course, this wouldn't be a nice API.