Up to Main Index                           Up to Journal for January, 2016

                    JOURNAL FOR SUNDAY 31TH JANUARY, 2016
______________________________________________________________________________

SUBJECT: Interfaces, nil and attribute finders
   DATE: Sun 31 Jan 21:27:31 GMT 2016

The Go FAQ[1] answers the question "Why is my nil error value not equal to
nil?". It goes on to explain that interfaces have a type and value and that
both need to be nil in order for an interface to compare to nil. Have a look
at the FAQ as it probably explains it better than I have.

Why do I mention this? While working on changes to WolfMUD's commands I was
adding the following snippet lot:


  // Get actor's name
  who := "Someone"
  if a := attr.FindName(s.actor); a != nil {
    who = a.Name()
  }

  s.msg.observer.WriteJoin(who, " examines ", name, ".")


This example is from the EXAMINE command. When someone examines something any
observers at the same location see something like "Diddymus examines a bug".

My first thought was to stash the actor's name into the state struct. However
I can see state becoming an overloaded bit bucket of data when ideally we want
to keep it as light as possible.

My second thought was that it would be nice to be able to use something like:


  who := attr.FindName(s.actor).Name()
  s.msg.observer.WriteJoin(who, " examines ", name, ".")


Problem one is that attr.FindName() can return the interface type has.Name or
nil. While you can call methods on nil pointers it does not work for nil
interfaces[2]. If the interface is nil there is no concrete type stored in the
interface to call Name() on. So instead of:


  func FindName(t has.Thing) has.Name {
    for _, a := range t.Attrs() {
      if a, ok := a.(has.Name); ok {
        return a
      }
    }
    return nil
  }


We need to change the last 'return nil' statement to be:


  return (*Name)(nil)


Here the returned interface type has.Name contains a concrete type *Name which
is set to nil - as opposed to the interface itself being nil. That is we have
(*Name,nil) and not (nil,nil).

This leads us to problem two. All functions with a receiver of *Name, such as
Name() in this example, would need to be able to handle a nil receiver. So
instead of:


  func (n *Name) Name() string {
    return n.name
  }


I know Name() here does not need to have a *Name receiver but it's part of the
bigger whole. We can check the receiver and return a default string in this
very simple example:


  func (n *Name) Name() string {
    if n == nil {
      return "someone or something"
    }
    return n.name
  }


Now for problem three. If finders can return a valid nil what is the best way
to check for nil still when we want to? For now I've added a Nil() method to
the Name interface. This seems to be working well, and does not need to
use the reflection package. Now we can do:


  if a := attr.FindName(what); !a.Nil() {
    // Do something interesting here...
  }


These changes would need to be done to all of the finders. Also to all of the
default implementations of the interfaces that those finders return. That is
quite a chunk of work. Is it worth it? I'm not sure yet. I'll have to make the
changes to all the current finders and attributes, update the commands and see
how much it simplifies things :(

This entry was initially written for Friday 29th. However due to some oddities
with embedded types things got delayed why I investigated. I'm still not sure
what is happening but didn't want to delay this entry any longer.

--
Diddymus

  [1] Go FAQ: https://golang.org/doc/faq

  [2] You can have a pointer to an interface but you usually never actually
      want that. I wonder if you can call methods on nil pointers to
      interfaces!? Another rabbit hole to go down...



  Up to Main Index                           Up to Journal for January, 2016