类型如何成为Go中的函数?

I am a Go rookie.

I am looking at some Kubernetes source code.

I see this:

// GetByKey returns the key if it exists in the list returned by kl.
func (kl keyLookupFunc) GetByKey(key string) (interface{}, bool, error) {
    for _, v := range kl() {
        if v.name == key {
            return v, true, nil
        }
    }
    return nil, false, nil
}

I know hazily how to read this, but I'm sure I'm going to get my terminology wrong: There's a type somewhere called keyLookupFunc, and kl is effectively an instance of it, and this function, named GetByKey can be called on it. It accepts a key whose type is string, and it returns three values, etc. etc.

(I don't see the BNF for this particular construction at my best guess as to where it should live in the language specification, but I've seen this construction several times before so I take it on faith.)

Higher up in the source code, I notice this:

// keyLookupFunc adapts a raw function to be a KeyLookup.
type keyLookupFunc func() []testFifoObject

OK, so indeed keyLookupFunc is a type, and it is used to describe something that is a function that takes zero parameters and returns a slice of testFifoObjects.

So naïvely, if I have a keyLookupFunc-typed variable in my hand, I should be able to call GetByKey "on" it. I am not entirely sure how a function can behave like a type in this situation, but I'll take it on faith.

Now when I look to see how this is all used, I see this partial stuff:

func TestDeltaFIFO_requeueOnPop(t *testing.T) {
    f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil)

    f.Add(mkFifoObj("foo", 10))
    _, err := f.Pop(func(obj interface{}) error {
        if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
            t.Fatalf("unexpected object: %#v", obj)
        }
        return ErrRequeue{Err: nil}
    })
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
        t.Fatalf("object should have been requeued: %t %v", ok, err)
    }

Note the f.GetByKey("foo") invocation. f is a pointer to a DeltaFIFO, which I just happen to know is returned by NewDeltaFIFO.

Given that f is a pointer to a DeltaFIFO, how can it also be a keyLookupFunc such that this code can call GetByKey "on" it? How do I connect these dots?

Note the twoGetByKey methods are two separate methods on two different types, (Two different types with a method with the same name):

Note the last f.GetByKey is:

// GetByKey returns the complete list of deltas for the requested item,
// setting exists=false if that list is empty.
// You should treat the items returned inside the deltas as immutable.
func (f *DeltaFIFO) GetByKey(key string) (item interface{}, exists bool, err error) {
    f.lock.RLock()
    defer f.lock.RUnlock()
    d, exists := f.items[key]
    if exists {
        // Copy item's slice so operations on this slice (delta
        // compression) won't interfere with the object we return.
        d = copyDeltas(d)
    }
    return d, exists, nil
}

and this called once here (in delta_fifo.go file at return f.GetByKey(key)):

// Get returns the complete list of deltas for the requested item,
// or sets exists=false.
// You should treat the items returned inside the deltas as immutable.
func (f *DeltaFIFO) Get(obj interface{}) (item interface{}, exists bool, err error) {
    key, err := f.KeyOf(obj)
    if err != nil {
        return nil, false, KeyError{obj, err}
    }
    return f.GetByKey(key)
}

And also called three times here (in delta_fifo_test.go file at f.GetByKey("foo")):

func TestDeltaFIFO_requeueOnPop(t *testing.T) {
    f := NewDeltaFIFO(testFifoObjectKeyFunc, nil, nil)

    f.Add(mkFifoObj("foo", 10))
    _, err := f.Pop(func(obj interface{}) error {
        if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
            t.Fatalf("unexpected object: %#v", obj)
        }
        return ErrRequeue{Err: nil}
    })
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
        t.Fatalf("object should have been requeued: %t %v", ok, err)
    }

    _, err = f.Pop(func(obj interface{}) error {
        if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
            t.Fatalf("unexpected object: %#v", obj)
        }
        return ErrRequeue{Err: fmt.Errorf("test error")}
    })
    if err == nil || err.Error() != "test error" {
        t.Fatalf("unexpected error: %v", err)
    }
    if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
        t.Fatalf("object should have been requeued: %t %v", ok, err)
    }

    _, err = f.Pop(func(obj interface{}) error {
        if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
            t.Fatalf("unexpected object: %#v", obj)
        }
        return nil
    })
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if _, ok, err := f.GetByKey("foo"); ok || err != nil {
        t.Fatalf("object should have been removed: %t %v", ok, err)
    }
}

And the other method is:

// GetByKey returns the key if it exists in the list returned by kl.
func (kl keyLookupFunc) GetByKey(key string) (interface{}, bool, error) {
    for _, v := range kl() {
        if v.name == key {
            return v, true, nil
        }
    }
    return nil, false, nil
}

and this is not used (not called).

Function is a first class citizen in Go: it can have a methods on it and even implement interfaces.

https://play.golang.org/p/d-N4OKLirQ