I am trying to port a generic library for Haskell to Scala. However, I am currently not really happy with how I solved the generic Crush function in Scala.
I defined to following treats to handle Crush functions.
trait FRep[G[_],F[_]]{
def frep[A](g1 : G[A]) : G[F[A]]
}
trait Crush[B,A]{
def selCrush : Assoc => A => B => B
}
Next, I wanted to define the crush function, but here I ran into problems. The problem is that I need this FRep trait to represent the Crush function, but the G(Generic) in frep only allows 1 parameter. I solved this using lambda types, but I still have some problems to define a function. This is the approach I wanted to work:
def crush[B,A,F[_]](asc : Assoc)(f : A => B => B)(z : B)(x : F[A])(implicit rep : FRep[({type AB[A] = Crush[B,A]})#AB,F]): B = {
def fCrush = new Crush[B,A]{
override def selCrush= _ => f
}
return(rep frep(fCrush).selCrush(asc)(x)(z))
}
This obviously gave an error, because the A parameter in the crush function is not the same as the A lambda type in the implicit rep variable, which needs to be same in order to use the Crush function. This is the error I received:
<pastie>:677: error: type mismatch;
found : x.type (with underlying type F[A])
required: A
return(rep frep(fCrush).selCrush(asc)(x)(z))
So, the solution I came up with is to split the crush function into more parts, such that I could use the same A for the crush function. This is the current solution that compiles:
class CrushFunction[B,F[_]](asc : Assoc)(z : B)(implicit rep : FRep[({type AB[A] = Crush[B,A]})#AB,F]){
def crush[A](f : A => B => B)(x : F[A]) : B = {
val crushVal = new Crush[B,A]{
override def selCrush: Assoc => A => B => B = _ => f
}
return(rep.frep(crushVal).selCrush(asc)(x)(z))
}
}
So, my question is: Is there a nicer way to solve this?
Actually the only thing wrong with your first solution is that you left out the dot between rep
and frep
. I would also advise you not to use return
explicitly, and not to shadow names of type parameters (A
):
def crush[B,A,F[_]](asc : Assoc)(f : A => B => B)(z : B)(x : F[A])(implicit rep : FRep[({type AB[X] = Crush[B,X]})#AB,F]): B = {
def fCrush = new Crush[B,A]{
override def selCrush: Assoc => A => B => B = _ => f
}
rep.frep(fCrush).selCrush(asc)(x)(z)
}
Now you may ask yourself: why this error message if all that's wrong is a missing dot? Well, scala supports infix notation. It parses a b c d e
as a.b(c).d(e)
. But you wrote something like this: a b(c).d(e)
. This confuses the parser. You can see what happens when you append //print
to your code in the REPL (version 2.11.8 or higher I think) and press the TAB key.
I filtered out all the cruft for you. rep frep(fCrush).selCrush(asc)(x)(z)
gets parsed as:
rep.frep[A](fCrush.selCrush(asc)(x)(z))
In that expression x
is indeed required to be of type A
instead of F[A]
.
Honestly: to me it seems a bit like a bug that scalac managed to parse your dotless code, completely ignoring a parenthesis in the process.
Let's call it an unfortunate syntax quirk :-)