Tyler
Tyler

Reputation: 18177

Pass function to generic function

I have the following code that I would like to abstract for additional types:

val users = Set[User](new User(1, "tyler"), new User(2, "phil"), new User(3, "quan"))
val groups = Set[Group](new Group(1, 10, 1), new Group(2, 20, 3))


def myFind(user: User)(implicit g: Group): Boolean = {
     user.id == g.adminID
}

val combined = groups.map(implicit g => {
    val admin = users.find(myFind)

    // Do something with relation
    g.toString + " " + admin.toString
})

println(combined)

What I would like to do is turn this into a function. What I have so far is:

def myFunc[A, B](s1: Set[A], s2: Set[B], f: (B) => Boolean): Set[String] = {
    s1.map(implicit t1 => {
        val rel = s2.find(f)
        t1.toString + " " + rel.to
    })
}

myFunc(users, groups, myFind)

There are two compilation errors:

Could not find implicit value for parameter g: Sets.Group
myFunc(users, groups, myFind)
               ^

And

Not enough arguments for method myFind: (implicit g: Sets.Group)Boolean.
Unspecified value parameter g.
myFunc(users, groups, myFind)
               ^

EDIT: Full code:

case class User(id: Int, name: String)
case class Group(id: Int, membersCount: Int, adminID: Int)

object Playground {

    def main(args: Array[String]) {

        val users = Set[User](new User(1, "tyler"), new User(2, "phil"), new User(3, "quan"))
        val groups = Set[Group](new Group(1, 10, 1), new Group(2, 20, 3))


        def myFind(user: User)(implicit g: Group): Boolean = {
            user.id == g.adminID
        }

        val combined = groups.map(implicit g => {
            val admin = users.find(myFind)

            // Do something with relation
            g.toString + " " + admin.toString
        })

        println(combined)

        myJoin(users, groups, myFind)

    }

    def myJoin[A, B](s1: Set[A], s2: Set[B], f: (B) => Boolean): Set[String] = {
        s1.map(implicit t1 => {
            val rel = s2.find(f)
            t1.toString + " " + rel.to
        })
    }
}

Upvotes: 1

Views: 67

Answers (1)

Kolmar
Kolmar

Reputation: 14224

The problem is that in myFunc at the point where you call s2.find(f) there is no information that f takes any implicit parameters. And you can't express this kind of information in the type signature of a function argument.

I believe, your best bet is to have myFunc take a function from both A and B to Boolean (either (A, B) => Boolean or A => B => Boolean).

For example:

def myFunc[A, B](s1: Set[A], s2: Set[B], f: A => B => Boolean): Set[String] =
  s1.map { t1 =>
    val rel = s2.find(f(t1))
      t1.toString + " " + rel.to
  }

Then for this definition of myFunc, you can pass myFind to it in one of those ways:

myFunc(users, groups, (u: User) => { implicit g: Group => myFind(u)})

Or

myFunc(users, groups, (u: User) => (g: Group) => myFind(u)(g))

Or define a function without implicit parameters and pass it directly:

def myFind2(user: User)(g: Group): Boolean = user.id == g.adminID
myFunc(users, groups, myFind2)

Or if you have a function from two arguments you can pass it to myFunc using curried method:

def myFind3(user: User, g: Group): Boolean = user.id == g.adminID
myFunc(users, groups, (myFind3 _).curried)

Upvotes: 2

Related Questions