Тип, который описывает любую go функцию - PullRequest
0 голосов
/ 03 мая 2020

Я хочу написать функцию, которая частично применяет функцию к аргументу, например:

func partial(f AnyFuncType, arg interface{}) AnyFuncType {
   return func(args ...interface{}) interface{} { 
       return f(arg, args)
   }
}

type AnyFuncType func(args ...interface{}) interface{}

Но это не работает даже с самой простой функцией, такой как

func sum(a int, b int) int {
  return a + b
} 

func main() {
  addToFive := partial(sum, 5)
}

потому что я получаю

./prog.go:16:23: cannot use sum (type func(int, int) int) as type AnyFuncType in argument to partial

ошибка компиляции. Теперь я знаю, что мог бы использовать интерфейс {}, но есть ли способ указать более точный тип для f, который будет работать с любой функцией?

Ответы [ 3 ]

4 голосов
/ 03 мая 2020

Вы пытаетесь обработать interface{} как универсальный тип c, но interface{} не является универсальным типом c, и go не будет соответствовать сигнатуре функции, которая принимает interface{} в качестве подпись функции, которая принимает конкретный тип.

2 голосов
/ 03 мая 2020

Используйте interface{} для представления функции любого типа. Не существует более точного типа, который работает с любой функцией.

Используйте пакет отражение для реализации partial.

func partial(f interface{}, arg interface{}) interface{} {
    v := reflect.ValueOf(f)
    t := v.Type()

    var in []reflect.Type
    for i := 1; i < t.NumIn(); i++ {
        in = append(in, t.In(i))
    }

    var out []reflect.Type
    for i := 0; i < t.NumOut(); i++ {
        out = append(out, t.Out(i))
    }

    var va reflect.Value
    if arg != nil {
        va = reflect.ValueOf(arg)
    } else {
        // Support `nil` as partial argument.
        va = reflect.Zero(t.In(0))
    }

    return reflect.MakeFunc(reflect.FuncOf(in, out, t.IsVariadic()),
        func(args []reflect.Value) []reflect.Value {
            return v.Call(append([]reflect.Value{va}, args...))
        }).Interface()
}

Используйте его следующим образом:

addToFive := partial(sum, 5).(func(int) int)
fmt.Println(addToFive(1))

Запустите его на игровой площадке .

Я рекомендую использовать замыкание для создания партиалов вместо функции partial в этом ответе. Закрытие более эффективно и позволяет избежать хитрого отражения кода.

addToFive := func(x int) int { return sum(5, x) }
fmt.Println(addToFive(1))
1 голос
/ 03 мая 2020

Проблема в том, что подтип в GO работает только для взаимодействий. Поскольку AnyFuncType не является интерфейсом, это не будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...