Scala function typing issue

2019-08-20 12:30发布

问题:

I'm given a list lis of type List[(Char,Int)] and need to sort it by chars, but I want do do this using sortWith function. One way to do this is using:

val newlis = lis.sortWith({case ((a,b),(c,d)) => a<c })

But when I wrote auxilary function:

def aux2(term: ((Char, Int),(Char, Int)) ): Boolean = term match {
  case ((a,b),(c,d)) => a<c
  case _ => false
}
lis.sortWith(aux2)

I get type mismatch. Why is that?

回答1:

There is a difference between

def f(a: A, b: B): C

and

def f(ab: (A, B)): C

When lifted and desugared, the first one becomes a Function2[A, B, C], while the second one becomes Function[(A, B), C]. While essentially the same (isomorphic), these two types must be used with a slightly different syntax.

Here is a slightly more complex example with (Char, Int) instead of A and B:

def g1(abcd: ((Char, Int), (Char, Int))): Boolean = abcd._1._2 < abcd._2._2
val g1Fun: (((Char, Int), (Char, Int))) => Boolean = g1
//         ^^^            ^
//         || \           second component of tuple: (Char, Int)
//         ||  first component of tuple: (Char, Int)
//         | \
//         |  single argument of type ((Char, Int),(Char, Int))
//          \
//           argument list of `g1Fun`, accepts *one* argument


def g2(ab: (Char, Int), cd: (Char, Int)): Boolean = ab._2 < cd._2
val g2Fun: ((Char, Int), (Char, Int)) => Boolean = g2
//         ^^            ^
//         ||            second argument of type (Char, Int)
//         | \
//         |  first argument of type (Char, Int)
//          \
//           argument list of `g2Fun`, needs *two* arguments

The function lt in sortWith behaves like the second function g2Fun, whereas the function aux2 that you have written is more like the g1Fun.


So, to fix your code, you have to define aux2 as a binary operation:

val lis: List[(Char,Int)] = List(('a', 34), ('b', 42))

println(lis.sortWith({case ((a,b),(c,d)) => a < c }))

def aux2(
  firstTerm: (Char, Int), 
  secondTerm: (Char, Int) 
): Boolean = (firstTerm, secondTerm) match {
  case ((a,b), (c,d)) => a < c
  case _ => false
}

println(lis.sortWith(aux2))

To make it work with the same arity of f as you have written, the sortWith would have to accept a slightly different sort of functions. Consider this:

def watchTheArity(f: ((Int, Int)) => Int): Int = {
  f((4, 5))
}

See the weird double parens wrapping the (Int, Int) part?

This means: "I expect an f that takes a single tuple as argument".

Therefore, this works:

def f(ab: (Int, Int)): Int = ab._1 + ab._2

println(watchTheArity(f))

However, if it were defined as

def watchTheArity2(f: (Int, Int) => Int): Int = {
  f(4, 5)
}

then you would need an f that takes two arguments.


Edit notice Added Ascii-art explanation of parentheses.



标签: scala