How to pass a function as an argument

Sometimes it is really difficult in Scala to pass a function as an argument.
Can someone help me find a more natural way to pass the 5th and 6th arguments
to this function. The Magma.isField method is has type parameter T, and its 5th and 6th
arguments have type T=>Option[T]. At the call site, I’m demanding that T be (Int, Int).
I.e., I want to create a closure which closes over the variable m.
I tried simply a => Some(m.subtract(m.zero, a)), in which case I get an error that a needs a type parameter. Then I change it to a:(Int,Int) => Some(m.subtract(m.zero, a)), I see that Some` needs a type parameter, and that the parser can no longer parse the code because of problems with parens and curly braces.

'}' expected but ',' found.
                           a:(Int,Int) => Some(m.subtract(m.zero, a)),

Here is the call:

object testGaussianInt {
  def main(argv: Array[String]): Unit = {
    for {p <- 2 to 4
         m = new GaussianIntModP(p)
         f = Magma.isField(m.gen, m.member,
                           m.add, m.mult,
                           locally {
                             def maybe_add_invert(a: (Int, Int)): Option[(Int, Int)] =
                               Some(m.subtract(m.zero, a))

                             maybe_add_invert _
                           },
                           locally {
                             def maybe_mult_invert(a: (Int, Int)): Option[(Int, Int)] =
                               m.gen().find(b => m.mult(a, b) == m.one)

                             maybe_mult_invert _
                           },
                           m.one, m.zero
                           )
         } println(s"$p $f")
  }
}

Ahh, I think I found a much better way.
The following seems to work. I need to give [(Int,Int)] as type parameter at the call site of Magma.isField.

object testGaussianInt {
  def main(argv: Array[String]): Unit = {
    for {p <- 2 to 4
         m = new GaussianIntModP(p)
         f = Magma.isField[(Int,Int)](m.gen, m.member,
                           m.add, m.mult,
                           a => Some(m.subtract(m.zero, a)),
                           a => m.gen().find(b => m.mult(a, b) == m.one),
                           m.one, m.zero
                           )
         } println(s"$p $f")
  }
}
1 Like

Without information about the other parameters of isField I can’t say for sure, but it may be possible to let the compiler infer the parameters, if you rearrange the signature.

In Scala 2 the compiler infers type parameters for methods per parameter list. So if you have a function like

def foo[A,B](a: A, f: A => B): B = f(a)

foo(3, a => a.toString)

the compiler will try to infer the types of all arguments first, and then use the result to infer A and B. This causes the missing type error, because the type of a => a.toString is inferred before A.

As the inference works per parameter list, splitting it up helps the compiler:

def foo[A,B](a: A)(f: A => B): B = f(a)

foo(3)(a => a.toString)

Here, the first parameter list contains only an A, which in the example call is inferred to Int. So when the second parameter list is inferred, A is known, i.e. the compiler expects a function taking an Int. This way the type of the lambda can be inferred.

From your example, I would guess that m.one and m.zero are both (Int, Int), while all the other parameters are functions, correct? If so, you could move those two into their own parameter list before the other parameters and it should make the types inferable.

N.B. this is often no longer needed in Scala 3.

yes, this is something I’m really happy about in Scala 3, that functions are not called simply by mentioning the name. This has troubled me since the beginning.