[SOLVED] Calling a generic function type: invalid operation: cannot call non-function fn

Issue

I’ll post the code example that I am currently working on. I got the error:

error: invalid operation: cannot call non-function fn (variable of type MF constrained by MapFunc)

Is it possible to use constraints that contain different function signatures to be used in this way? (I would like to understand how to get what I have written to work first if at all possible.)

Here is the example:

package main

import "fmt"

// MapFunc constraint used in Funcy
type MapFunc interface {
    func(s string, ss []string) []string | func(s string, ss []string) []bool
}

// MapFuncType used for Funcy return constraint
type MapFuncType interface {
    string | bool
}

// Funcy preforms map operation on generic map functions
func Funcy[MF MapFunc, MFT MapFuncType](s string, ss []string, fn MF) []MFT {
    return fn(s, ss)
    // error: invalid operation: cannot call non-function fn (variable of type
    // MF constrained by MapFunc)
}

// appendTo adds given string to the end of each index of given slice of strings
// Ex. appendTo("_", []string{"append", "to"}) --> []string{"append_", "to_"}
func appendTo(s string, ss []string) []string {
    var slice []string
    for _, v := range ss {
        slice = append(slice, v+s)
    }
    return slice
}

// isMatch checks given string against each index in given string slice for a
// match
// Ex. isMatch("hi", []string{"hello", "hi"}) --> []bool{false, true}
func isMatch(s string, ss []string) []bool {
    var slice []bool
    for _, v := range ss {
        slice = append(slice, s == v)
    }
    return slice
}

func main() {
    slice1 := []string{"append", "to"}
    slice2 := []string{"hello", "hi"}

    fmt.Println(Funcy(slice1, appendTo))
    // want: []string{"append_", "to_"}
    // got: error: cannot infer MFT

    fmt.Println(Funcy(slice2, isMatch))
    //[]bool{false, true}
    // got: error: cannot infer MFT
}

Solution

When you constrain the type param MF to MapFunc, you can’t call fn because the function types in MapFunc‘s type set don’t have the same signature. They have different return types []string and []bool.

So the variable of type fn effectively does not support being called, and you get the (slightly cryptic) error message "cannot call non-function fn".

More formally, only a value that has a core type of type function can be called, but the constraint MapFunc doesn’t have a core type.

The fix would be to parametrize MapFunc and use that type parameter as the return value, so that upon instantiation, it would have a core type:

type MapFunc[T MapFuncType] interface {
    func(s string, ss []string) []T
}

And instantiate the constraint with MFT in Funcy:

func Funcy[MF MapFunc[MFT], MFT MapFuncType](s string, ss []string, fn MF) []MFT {
    return fn(s, ss)
}

Playground: https://go.dev/play/p/q88aJLgnzXZ

Answered By – blackgreen

Answer Checked By – Clifford M. (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published.