Trying to implement CL:MAPCAN in scala


#1

It could be that this is again related to the left to right type inferencing limitation of the scala compiler as in More help understanding the _ variable
but let me ask it anyway.

In the code below, I’m attempting to write several arity versions of mapcan, which should behave similar to flatMap but with higher arity. The compiler errors are confusing to me.

  def mapcan[A1,A2,B](f:(A1,A2)=>List[B],L1:List[A1],L2:List[A2]):List[B] = {
    (L1,L2).zipped.flatMap(f)
  }
  def mapcan[A1,A2,A3,B](f:(A1,A2,A3)=>List[B], L1:List[A1],L2:List[A2],L3:List[A3]):List[B] = {
    (L1,L2,L3).zipped.flatMap(f)
  }

  def list3[K](a:K,b:K,c:K):List[K] = {
    List(a,b,c)
  }
  def list2[K](a:K,b:K):List[K] = {
    List(a,b)
  }

  mapcan(list3, List(1,2,3,4), List(10,20,30,40), List(100,200,300,400))
  mapcan(list2, List(1,2,3,4), List(10,20,30,40))

The compiler messages are these:

Error:(41, 10) type mismatch;
 found   : (Nothing, Nothing, Nothing) => List[Nothing]
 required: (Int, Int, Int) => List[Nothing]
  mapcan(list3, List(1,2,3,4), List(10,20,30,40), List(100,200,300,400))
Error:(42, 10) type mismatch;
 found   : (Nothing, Nothing) => List[Nothing]
 required: (Int, Int) => List[Nothing]
  mapcan(list2, List(1,2,3,4), List(10,20,30,40))

What confounds the situation even further is that IntelliJ highlights different errors than the compiler reports.
IntelliJ claims the following. Is this just an IntelliJ bug where it does not respect the same errors as the compiler?

Cannot resolve overloaded method mapcan.  
Too many arguments for method mapcan(A1=>List[B],List[A1])
Too many arguments for method mapcan((A1,A2)=>List[B],List[A1],List[A2])
Unspecified value parameters:L3:List[NotInferredA3]
Too many arguments for method mapcan(A1=>List[B],List[A1])

BTW, my attempt here is to implement a Scala approximation of the Common Lisp function: MAPCAN
specified here: http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm
My plan is to implement the 6 functions (mapc, mapcar, mapcan, mapcon, mapl, maplist) first for List, thereafter to extend the implementation to other collection types.


#2

Perhaps it is the same problem as More help understanding the _ variable

because when I added additional implementations of list2 and list3, the compiler errors go away

  def list3(a:Int,b:Int,c:Int):List[Int] = {
    List(a,b,c)
  }
  def list2(a:Int,b:Int):List[Int] = {
    List(a,b)
  }

#3

Broadly speaking, that’s correct. IntelliJ has its own version of the compiler that it uses for presentation errors. While that’s pretty good, it does not reliably match the real compiler in the details. I recommend always compiling with sbt when in doubt.


#4

More generally: have you looked into Shapeless? That provides a lot of tools for abstracting over arity, which isn’t simple in Scala. At the very least, it’s probably worth examining how it tackles problems like this…


#5

I’ve seen some presentations on shapeless, but it is sufficiently beyond my level of understanding at the moment.


#6

I’d strongly recommend getting a copy of The Type Astronaut’s Guide to Shapeless. It’s pretty short, and while it doesn’t make it trivial to use Shapeless, I think it does a good job of demystifying it. It’s worth checking out…