0

Despite thinking and reading for several hours I don't understand the error produced by the following code when compiled with version 2.11.6 of the Scala compiler:

object Test
{
  type F = (String) => Set[String]

  def n_ary(f: (F, F) => F) = {
    def n_ary_f(x: F, args: F*): F =
      (t: String) =>
        if (args.isEmpty) x(t)
        else f(x, n_ary_f(args.head, args.tail: _*))(t)

    n_ary_f _
  }

  def seq(x: F, y: F) =
    (t: String) =>
      (for(c <- x(t).toIterator; d <- y(c).toIterator)
        yield d).toSet

  def seq_n(x: F, y: F*): F =
    n_ary(seq)(x, y: _*)
}

The compiler's output is:

params.scala:17: error: type mismatch;
 found   : Seq[Test.F]
    (which expands to)  Seq[String => Set[String]]
 required: Seq[Seq[Test.F]]
    (which expands to)  Seq[Seq[String => Set[String]]]
    n_ary(seq)(x, y: _*)
                  ^

When compiling the code without the _* behind the argument y in the call to n_ary(sec) on the last line there is no error. Nethertheless this seems kind of wrong to me. Version 2.9.2 of the Scala compiler actually produces the following error output when used to compile my code after applying the aforementioned modification to the last line:

params.scala:21: error: type mismatch;
 found   : String => Set[String]*
 required: String => Set[String]
    n_ary(seq)(x, y)
                  ^

In my opinion this error message makes sense, because n_ary's second argument is not a Seq[String => Set[String]], but a String => Set[String], that could also be left out or repeated once or more times. But to avoid this error I would have to undo my change and use the code as listed above leaving me with the aforementioned error when compiled with the current version of scalac ...

Could someone please explain what I am getting wrong here, especially concerning the error produced by the current compiler version?

mkrimpel
  • 23
  • 4
  • It's very unclear to me what you're trying to do, and I think you could probably simplify your types a lot, but two suggestions: use `:t n_ary _` to make sure the type of `n_ary` is what you think it is, because my guess is it probably isn't; don't use the same name for the two `seq` methods (the second of which is recursive—it doesn't refer to the first). – Travis Brown May 25 '15 at 00:22
  • I have to drop down to Java 7 to get both compiling, but I think the problem is related to this -> http://rants.tempura.org/2013/06/10/scala-2.10-and-varargs.html . Without compiling code and decompiling it, I believe that it has to do with the switch from * to Seq and possibly the way the apply method is written under the covers. – Justin Pihony May 25 '15 at 03:32
  • @ Travis Brown I apologize for confusing you. Of course I could have simplified my code by introducing a type alias as defined by jmaschad. I edited it to catch up on that. The second seq is not recursive, but referring to the first in its call to n_ary. The compiler chooses the first because of its matching type signature. Nethertheless I renamed the second one to `seq_n`. My original problem stays the same, I fear. – mkrimpel May 25 '15 at 09:32
  • @TravisBrown Seems that I am not allowed to use a space character between `@` and a user name .... – mkrimpel May 25 '15 at 09:59
  • @JustinPihony Thank you for pointing me to that link. Together with jmaschad's answer this made me understand what's going on. – mkrimpel May 25 '15 at 10:10

1 Answers1

0

I introduced a type alias and appended function types as inferred by Scala to get a clearer picture of your code.

  type F = (String) => Set[String]

  def n_ary(f: (F, F) => F): (F, Seq[F]) => F

n_ary returns a function with type (F, Seq[F]) => F so the y in n_ary(seq)(x, y) has to have type Seq[F]. What you probably wanted to do is to have n_ary return a function with repeated parameters, but even if you set the return type of n_ary to (F, F*) => F it does not work.

Reading the spec on function types you realize that all function types are some variant of Function_n[-T1 , … , -Tn, +R]. Functions with a repeated parameter would have an unknown number of parameter types T and could therefore not be typed in Scala's system.

The repeated parameter syntax seems to be a special trick on parameter lists of named functions which does not play nice with function objects (consider the accepted answer for this this related SO question)

Community
  • 1
  • 1
Johannes Luong
  • 574
  • 5
  • 19
  • Thank you for answering my question. If I understand you right, a repeated parameter like `F*` is represented as a `Seq[F]` not only in the body of the method or function receiving it but also after returning this function in another function (for currying). Sorry for not up-voting, not enough rep :-( – mkrimpel May 25 '15 at 10:20
  • Exactly, please have a look at the related question I found – Johannes Luong May 25 '15 at 11:58
  • Aha, so according to the linked post the use of `*` to annotate a repeated parameter is only allowed in the declaration of a named function, and passing an argument of a Seq-type with `_*` appended to it is only possible (and necessary) when calling such a function using its name. Thank you for helping. – mkrimpel May 25 '15 at 13:19