Event-triggering - Ready, On, Fire
Your description of the problem indicates that each event type can have multiple listeners, but the code only implements one listener for each event. Is this intended?

The params awkwardness is a "feature" of go....

Because Go is


statically compiled
does not have function signature overloading (you can't have multiple functions with the same name, and differentiate them based on the signature) 


You cannot avoid having the param input arguments to the Fire function. This is a "drawback" of Go. When you become more comfortable with the language you avoid constructs like you have, because they are hard to maintain (anything requiring reflect is probably a bad idea).

Additionally, your sync usage is broken - you need to manage the lock on both the Fire call, and the On call. Your On call is also broken because it has a race condition....

Note that reads on the map also need to be sync'd, not just writes.

So, this code:


func (t *trigger) On(event string, task interface{}) error {
    if _, ok := t.functionMap[event]; ok {
        return errors.New("event already defined")
    }
    if reflect.ValueOf(task).Type().Kind() != reflect.Func {
        return errors.New("task is not a function")
    }
    t.mu.Lock()
    defer t.mu.Unlock()
    t.functionMap[event] = task
    return nil
 }



should be:

func (t *trigger) On(event string, task interface{}) error {
    // Lock the mutex before reading it.
    t.mu.Lock()
    defer t.mu.Unlock()

    if _, ok := t.functionMap[event]; ok {
        return errors.New("event already defined")
    }
    if reflect.ValueOf(task).Type().Kind() != reflect.Func {
        return errors.New("task is not a function")
    }
    t.functionMap[event] = task
    return nil
 }


Additionally, the Fire should also have a mutex control (perhaps the t.read(...) function has a lock?)


func (t *trigger) Fire(event string, params ...interface{}) ([]reflect.Value, error) {
    f, in, err := t.read(event, params...)
    if err != nil {
        return nil, err
    }
    result := f.Call(in)
    return result, nil
 }