Scala: abstracting over a path-dependent type in impilicit parameter - scala

Let's say I have a class:
abstract class NumericCombine[A:Numeric,B:Numeric]{
type AB <: AnyVal
}
I want to define a function that returns a value of type NumericCombine[A,B].AB. for instance:
def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB
but the compiler doesn't let me reference .AB in plus.
FYI, this is the context of this question.
I want to provide:
implicit object IntFloat extends NumericCombine[Int,Float]{override type AB = Float}
implicit object FloatInt extends NumericCombine[Float,Int]{override type AB = Float}
and its other 44 friends (7*6-2) so that I can define my plus as below:
def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB =
{
type AB = Numeric[NumericCombine[A,B].AB]
implicitly[AB].plus(x.asInstanceOf[AB],y.asInstanceOf[AB])
}
plus(1f,2)//=3f
plus(1,2f)//=3f
I am aware of the fact that value conversions in Scala allows me to define
def plus[T](a: T, b: T)(implicit ev:Numeric[T]): T = ev.plus(a,b)
and achieve the behaviour above as suggested here, but since I want to use this function as part of a bigger function (which is described in the link mentioned as the context of this question), I need to parametrize the function with both A and B.
Update:
I made some good progress with this.
My NumericCombine now looks like this:
abstract class NumericCombine[A: Numeric, B: Numeric] {
type AB <: AnyVal
def fromA(x: A): AB
def fromB(y: B): AB
val numeric: Numeric[AB]
def plus(x: A, y: B): AB = numeric.plus(fromA(x), fromB(y))
def minus(x: A, y: B): AB = numeric.minus(fromA(x), fromB(y))
def times(x: A, y: B): AB = numeric.times(fromA(x), fromB(y))
}
and My plus function looks like:
def plus[A: Numeric, B: Numeric](x: A, y: B)(implicit ev:NumericCombine[A,B])
: ev.AB = ev.plus(x, y)
The weighted average function requiring plus ended up becoming a bit more complicated:
def accumulateWeightedValue[A: Numeric,B: Numeric]
(accum: (A, NumericCombine[A, B]#AB), ValueWithWeight: (A, B))
(implicit combine: NumericCombine[A, B], timesNumeric: Numeric[NumericCombine[A, B]#AB])
:(A,NumericCombine[A, B]#AB)=
this is a function that takes (A,AB),(A,B) and returns (A,AB). I use it internally inside weightedSum which just aggregates over this:
def weightedSum[A: Numeric,B: Numeric](weightedValues: GenTraversable[(A, B)])
(implicit numericCombine: NumericCombine[A, B], plusNumeric: Numeric[NumericCombine[A, B]#AB])
: (A, NumericCombine[A, B]#AB)
Now, this compiles fine. It does seem to have a problem with the second implicit parameter. ie Numeric[AB] when I run it with implicit values for say NumericCombine[Int,Float] present. It gives me:
could not find implicit value for parameter plusNumeric:
Numeric[NumericCombine[Int,Float]#AB]
note that in NumericCombine, I have a Numeric[AB] which should be available for implicit look-up. storing it locally, in the case of [Int,Float]:
val lst: Seq[(Int, Float)] =List((1,3f),(1,4f))
implicit val num: Numeric[Float] = IntFloat.numeric //IntFloat extends NumericCombine[Int,Float]
weightedSum(lst)
in a local variable before invoking the function needing it doesn't seem to have any impact. So why is it being picked up by the implicit system.

Just use
def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B]#AB
Note the # (hash) instead of . (dot). This is called "type projection". Dot notation is called "path dependent type". I'm telling you these names so that you can google for more info easily. Simply put, # is used for accessing types from classes/traits, and . is used for accessing types from objects/values.
Example:
trait Foo {
type T
}
val fooObj: Foo = new Foo {
type T = Int
}
type t1 = fooObj.T
type t2 = Foo#T

* 18 Apr 2017: updated base on the latest code from author *
* 19 Apr 2017 *
Add NumericCombine#Implicits for convinence
Remove AnyVal constraints to support any Numeric types e.g. BigInt
Refactor NumericCombine
You need Aux pattern:
import scala.collection.GenSeq
trait NumericCombine[A, B] {
type AB
def fromA(x: A): AB
def fromB(y: B): AB
val numericA: Numeric[A]
val numericB: Numeric[B]
val numericAB: Numeric[AB]
// For convenience, caller can 'import combine.Implicits._'
// to bring the Numeric's into the current scope
object Implicits {
implicit def implicitNumericA = numericA
implicit def implicitNumericB = numericB
implicit def implicitNumericAB = numericAB
}
def plus(x: A, y: B): AB = numericAB.plus(fromA(x), fromB(y))
def minus(x: A, y: B): AB = numericAB.minus(fromA(x), fromB(y))
def times(x: A, y: B): AB = numericAB.times(fromA(x), fromB(y))
}
object NumericCombine {
type Aux[A, B, _AB] = NumericCombine[A, B] {
type AB = _AB
}
private def combine[A, B, _AB](fa: A => _AB, fb: B => _AB)
(implicit
_numericA: Numeric[A],
_numericB: Numeric[B],
_numericAB: Numeric[_AB]
): NumericCombine[A, B] = new NumericCombine[A, B] {
override type AB = _AB
override def fromA(x: A): AB = fa(x)
override def fromB(y: B): AB = fb(y)
override val numericA: Numeric[A] = _numericA
override val numericB: Numeric[B] = _numericB
override val numericAB: Numeric[AB] = _numericAB
}
implicit lazy val IntFloat = combine[Int, Float, Float](_.toFloat, identity)
implicit lazy val BigIntBigDecimal = combine[BigInt, BigDecimal, BigDecimal](i => BigDecimal(i), identity)
}
implicit class ValuesWithWeight[A, B](val weightedValue: (A, B)) {
def weight: A = weightedValue._1
def value: B = weightedValue._2
}
def weightedSum[A, B, AB]
(valuesWithWeight: GenSeq[(A, B)])
(implicit combine: NumericCombine.Aux[A, B, AB]):
(A, AB) = {
import combine.Implicits._
val z: (A, AB) =
(combine.numericA.zero, combine.numericAB.zero)
def accumulateWeightedValue(accum: (A, AB), valueWithWeight: (A, B)): (A, AB) = {
val weightedValue = combine.times(valueWithWeight.weight, valueWithWeight.value)
(
combine.numericA.plus(accum.weight, valueWithWeight.weight),
combine.numericAB.plus(accum.value, weightedValue)
)
}
valuesWithWeight.aggregate(z)(
accumulateWeightedValue,
// dataOps.tuple2.plus[A,AB]
{
case ((a1, ab1), (a2, ab2)) =>
(combine.numericA.plus(a1, a2) ->
combine.numericAB.plus(ab1, ab2))
}
)
}
weightedSum(Seq(1 -> 1.5f, 2 -> 1f, 3 -> 1.7f))
weightedSum(Seq(BigInt(1) -> BigDecimal("1.5"), BigInt(2) -> BigDecimal("1"), BigInt(3) -> BigDecimal("1.7")))

An alternative to #slouc's answer is
def plus[A, B](x: A, y: B)(implicit ev: NumericCombine[A, B]): ev.AB
I'd also enhance NumericCombine:
trait NumericCombine[A, B] {
type AB <: AnyVal
def fromA(a: A): AB
def fromB(b: B): AB
val num: Numeric[AB]
}
abstract class NumericCombineImpl[A, B, R](implicit val num: Numeric[R], f1: A => R, f2: B => R) {
type AB = R
def fromA(a: A) = f1(a)
def fromB(b: B) = f2(b)
}
implicit object IntFloat extends NumericCombineImpl[Int,Float,Float]
...
This would allow to actually implement plus, no casts required:
def plus[A, B](x: A, y: B)(implicit ev: NumericCombine[A, B]): ev.AB =
ev.num.plus(ev.fromA(x), ev.fromB(y))

Related

Covariance between 3 types in Scala

I'm trying to see if there is a way to find a type W2 that is the super type of 2 types W and E.
In my solution E represents errors, and W represents Warnings.
What I'm trying to accomplish is a method or that if this fails runs that and moves the error to the warning type.
Here is a simplified example of what I'm doing.
sealed trait Validator[-I, +E, +W, +A] extends Product with Serializable
this type has several cases, which aren't really important here, so instead I'll go over an example usage:
case class MyObj(coords: GeoCoords)
case class GeoCoords(lat: String, long: String)
// Getters
val getLatitude: Validator[GeoCoords, Nothing, Nothing, String] = from[GeoCoords].map(_.lat)
val getLongitude: Validator[GeoCoords, Nothing, Nothing, String] = from[GeoCoords].map(_.long)
val parseLatitude: Validator[GeoCoords, Exception, Nothing, Double] = getLatitude andThen nonEmptyString andThen convertToDouble
val parseLongitude: Validator[GeoCoords, Exception, Nothing, Double] = getLongitude andThen convertToDouble
Now this is a bad example, but what I'm looking to do is that because parseLatitude has an error type of Exception, perhaps I want to give a default instead, but still understand that it failed. I'd like to move the Exception from the E error param to the W warning param like this:
val defaultLatitude: Validator[GeoCoords, Nothing, Nothing, Double] = success(0)
val finalLatitude: Validator[GeoCoords, Nothing, Exception, Double] = parseLatitude or defaultLatitude
But as well, if instead of supplying default, the other action I take after the or may fail as well, therefore this should also be the case:
val otherAction: Validator[GeoCoords, Throwable, Nothing, Double] = ???
val finalLatitude: Validator[GeoCoords, Throwable, Exception, Double] = parseLatitude or otherAction
I've tried implementing or in several ways on the Validator type but everytime it gives me an issue, basically casting things all the way up to Any.
def or[I2 <: I, E2 >: E, W2 >: W, B >: A](that: Validator[I2, E2, W2, B]): Validator[I2, E2, W2, B] = Validator.conversion { (i: I2) =>
val aVal: Validator[Any, E, W, A] = this.run(i)
val bVal: Validator[Any, E2, W2, B] = that.run(i)
val a: Vector[W2] = aVal.warnings ++ bVal.warnings
// PROBLEM HERE
val b: Vector[Any] = a ++ aVal.errors
Result(
aVal.warnings ++ aVal.errors ++ bVal.warnings,
bVal.value.toRight(bVal.errors)
)
}
I need to be able to say that W2 is both a supertype of W and of E so that I can concatenate the Vectors together and get type W2 at the end.
A super simplified self contained example is:
case class Example[+A, +B](a: List[A], b: List[B]) {
def or[A2 >: A, B2 >: B](that: Example[A2, B2]): Example[A2, Any] = {
Example(that.a, this.a ++ this.b ++ that.a)
}
}
object stackoverflow extends App {
val example1 = Example(List(1, 2), List(3, 4))
val example2 = Example(List(5, 6), List(7, 8))
val example3 = example1 or example2
}
Where I want the output type of or to be Example[A2, B2] instead of Example[A2, Any]
Actually you want the concatenation of a List[A] and a List[B] to produce a List[A | B], where A | B is union type.
How to define "type disjunction" (union types)?
In Scala 2 union types are absent but we can emulate them with type classes.
So regarding "super simplified example" try a type class LUB (least upper bound)
case class Example[A, B](a: List[A], b: List[B]) {
def or[A2 >: A, B2, AB](that: Example[A2, B2])(
implicit
lub: LUB.Aux[A, B, AB],
lub1: LUB[AB, A2]
): Example[A2, lub1.Out] = {
Example(that.a, (this.a.map(lub.coerce1) ++ this.b.map(lub.coerce2)).map(lub1.coerce1(_)) ++ that.a.map(lub1.coerce2(_)))
}
}
val example1: Example[Int, Int] = Example(List(1, 2), List(3, 4))
val example2: Example[Int, Int] = Example(List(5, 6), List(7, 8))
val example3 = example1 or example2
// example3: Example[Int, Any] // doesn't compile
example3: Example[Int, Int] // compiles
trait LUB[A, B] {
type Out
def coerce1(a: A): Out
def coerce2(b: B): Out
}
trait LowPriorityLUB {
type Aux[A, B, Out0] = LUB[A, B] { type Out = Out0 }
def instance[A, B, Out0](f: (A => Out0, B => Out0)): Aux[A, B, Out0] = new LUB[A, B] {
override type Out = Out0
override def coerce1(a: A): Out0 = f._1(a)
override def coerce2(b: B): Out0 = f._2(b)
}
implicit def bSubtypeA[A, B <: A]: Aux[A, B, A] = instance(identity, identity)
}
object LUB extends LowPriorityLUB {
implicit def aSubtypeB[A <: B, B]: Aux[A, B, B] = instance(identity, identity)
implicit def default[A, B](implicit ev: A <:!< B, ev1: B <:!< A): Aux[A, B, Any] =
instance(identity, identity)
}
// Testing:
implicitly[LUB.Aux[Int, AnyVal, AnyVal]]
implicitly[LUB.Aux[AnyVal, Int, AnyVal]]
implicitly[LUB.Aux[Int, String, Any]]
implicitly[LUB.Aux[Int, Int, Int]]
// implicitly[LUB.Aux[Int, AnyVal, Any]] // doesn't compile
<:!< is from here:
Scala: Enforcing A is not a subtype of B
https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala#L48-L52
If you want Example to be covariant
case class Example[+A, +B]...
make LUB and LUB.Aux contravariant
trait LUB[-A, -B]...
type Aux[-A, -B, Out0] = LUB[A, B] { type Out = Out0 }

Tagless final example in Scala requires superfluous second interp arg

I'm playing around with implementing a tagless final DSL & interpreter in Scala, based on this blog post written in Haskell.
I can get an example running - see code below, but I don't quite understand why I need testVal(Interp)(Interp). If I only supply a single Interp argument, then I get the following compile errors:
Error:(29, 24) could not find implicit value for evidence parameter of type Test.Expr[Test.Id]
val x = testVal(Interp)
Error:(29, 24) not enough arguments for method testVal: (implicit evidence$1: Test.Expr[Test.Id])Test.Id[Int].
Unspecified value parameter evidence$1.
val x = testVal(Interp)
Is there a simple way to eliminate one of the Interp arguments?
object Test {
trait Expr[F[_]] {
def const(i: Int): F[Int]
def lam[A, B](f: F[A] => F[B]): F[A => B]
def app[A, B](f: F[A => B], a: F[A]): F[B]
def add(x: F[Int], y: F[Int]): F[Int]
}
type Id[A] = A
object Interp extends Expr[Id] {
override def const(i: Int): Id[Int] = i
override def lam[A, B](f: Id[A] => Id[B]): Id[A => B] = f
override def app[A, B](f: Id[A => B], a: Id[A]): Id[B] = f(a)
override def add(x: Id[Int], y: Id[Int]): Id[Int] = x + y
}
def testVal[F[_]: Expr](f: Expr[F]): F[Int] =
f.app(
f.lam[Int, Int](
x => f.add(x, f.const(1))),
f.const(10)
)
def main(args: Array[String]): Unit = {
// val x = testVal(Interp) -- won't compile
val x = testVal(Interp)(Interp)
println(x)
}
}
The syntax
def f[X: Y](args: Types): Res = { ... }
is a shortcut for
def f[X](args: Types)(implicit yx: Y[X]): Res = { ... }
so if you write
def testVal[F[_]: Expr](f: Expr[F]): F[Int] = { ... }
then it's the same as if you wrote
def testVal[F[_]](f: Expr[F])(implicit redundant: Expr[F]): F[Int] = { ... }
but you obviously don't need the same Expr[F] twice.
The signature should be either
def testVal[F[_]: Expr]: F[Int]
or
def testVal[F[_]](implicit f: Expr[F]): F[Int]
but not both at the same time.
Here is a full example, which also shows how to get the f using implicitly in the case that you decide to use the F: Expr variant (which does not assign a name to the implicit argument):
import scala.language.higherKinds
object Test {
trait Expr[F[_]] {
def const(i: Int): F[Int]
def lam[A, B](f: F[A] => F[B]): F[A => B]
def app[A, B](f: F[A => B], a: F[A]): F[B]
def add(x: F[Int], y: F[Int]): F[Int]
}
type Id[A] = A
object Interp extends Expr[Id] {
override def const(i: Int): Id[Int] = i
override def lam[A, B](f: Id[A] => Id[B]): Id[A => B] = f
override def app[A, B](f: Id[A => B], a: Id[A]): Id[B] = f(a)
override def add(x: Id[Int], y: Id[Int]): Id[Int] = x + y
}
def testVal[F[_]: Expr]: F[Int] = {
implicit val f = implicitly[Expr[F]]
f.app(
f.lam[Int, Int](
x => f.add(x, f.const(1))),
f.const(10)
)
}
def main(args: Array[String]): Unit = {
val x = testVal(Interp)
println(x)
}
}
Moreover, if you make Interp itself implicit, then you can omit all argument lists when invoking testVal, and instead write just
val x = testVal // no arguments at all.

Generically morph a class into another using Shapeless + LabelledGenerics

I know using Shapeless I can do something like this:
import shapeless._, syntax.singleton._, record._
case class Foo(x: Int, y: String)
case class RichFoo(x: Int, y: String, z: Double)
def makeRich(foo: Foo): RichFoo = {
val x = ('z ->> 0.9)
val repr = LabelledGeneric[Foo].to(foo) + x
LabelledGeneric[RichFoo].from(repr)
}
val a = Foo(1, "hello")
val b = makeRich(a)
Now I want to write a generic way to do this:
trait Morph[A, B, AR, BR] {
def apply(a: A)(f: AR => BR): B
}
object Morph {
implicit def genericMorph[A, B, AR, BR](implicit genA: LabelledGeneric.Aux[A, AR], genB: LabelledGeneric.Aux[B, BR]): Morph[A, B, AR, BR] =
new Morph[A, B, AR, BR] {
override def apply(a: A)(f: AR => BR) = genB.from(f(genA.to(a)))
}
implicit class Syntax[A](a: A) {
def morph[AR, BR, B](f: AR => BR)(implicit morph: Morph[A, B, AR, BR]): B =
morph(a)(f)
}
}
But, now the usage is unweidly?
val a = Foo(1, "hello")
a.morph[???, ???, RichFoo](_ + ('z ->> 0.9))
What is a better way to design this API?
I tried something like this:
implicit class Syntax[A](a: A) {
def morphTo[B] = new {
def using[AR <: HList, BR <: HList](f: AR => BR)(implicit morph: Morph[A, B, AR, BR]): B =
morph(a)(f)
}
}
a.morphTo[RichFoo].using(_ :+ ('z ->> 0.9))
But it does not really work
There are two restrictions that prevent type inference from working the way you want in your example (both have nothing to do with shapeless btw):
In current scalac specifying type parameters explicitly is all or nothing. But you want to specify only B leaving the rest to be inferred. Currying is one solution to this problem. So your attempt was on the right track, but didn't account for 2.
Type inference for method parameters flows from left to right one parameter list at a time. But you want to infer the type of f based on the type of morph which comes last because it is implicit. The solution here is... Currying again.
So from 1. and 2. it follows that you must curry twice:
implicit class Syntax[A](a: A) {
def morphTo[B] = new {
def by[AR <: HList, BR <: HList](implicit morph: Morph[A, B, AR, BR]) = new {
def using(f: AR => BR): B = morph(a)(f)
}
}
}
a.morphTo[RichFoo].by.using(_ :+ ('z ->> 0.9))
There is an alternative solution to 1. - use a dummy argument to specify the type parameter B:
trait To[-A]
object To {
private val instance = new To[Any] { }
def apply[A]: To[A] = instance
}
implicit class Syntax[A](a: A) {
def morph[B, AR <: HList, BR <: HList](to: To[B])(
implicit morph: Morph[A, B, AR, BR]
) = new {
def using(f: AR => BR): B = morph(a)(f)
}
}
a morph To[RichFoo] using (_ :+ ('z ->> 0.9))
For future reference on how these issues are addressed in Dotty:
You can already partially specify type parameters: a.morph[B = RichFoo]
There is ongoing work on more consistent implicit parameter syntax: lampepfl/dotty#1260
Edit: Usually it's a good idea to define types that depend on other types as type members:
trait Morph[A, B] {
type AR
type BR
def apply(a: A)(f: AR => BR): B
}
object Morph {
type Aux[A, B, AR0, BR0] = Morph[A, B] {
type AR = AR0
type BR = BR0
}
implicit def genericMorph[A, B, AR0, BR0](
implicit genA: LabelledGeneric.Aux[A, AR0], genB: LabelledGeneric.Aux[B, BR0]
): Aux[A, B, AR0, BR0] = new Morph[A, B] {
type AR = AR0
type BR = BR0
def apply(a: A)(f: AR => BR) = genB.from(f(genA.to(a)))
}
implicit class Syntax[A](a: A) {
def morphTo[B](implicit morph: Morph[A, B]) = new {
def using(f: morph.AR => morph.BR) = morph(a)(f)
}
}
}
import shapeless._, syntax.singleton._, record._
case class Foo(x: Int, y: String)
case class RichFoo(x: Int, y: String, z: Double)
class Morph[A, B, AR](a: A, genA: LabelledGeneric.Aux[A, AR]) {
def apply[BR](f: AR => BR)(implicit genB: LabelledGeneric.Aux[B, BR]) = genB.from(f(genA.to(a)))
}
implicit class Syntax[A, AR](val a: A)(implicit genA: LabelledGeneric.Aux[A, AR]) {
def morph[B]: Morph[A, B, AR] = new Morph(a, genA)
}
val a = Foo(1, "hello")
a.morph[RichFoo](_ + ('z ->> 0.9)) // => RichFoo(1,hello,0.9)
Based on #g.krastev's answer I went with this DSL approach:
import shapeless._, syntax.singleton._, record._, ops.hlist._
case class Morph[A, AR](a: A)(implicit reprA: LabelledGeneric.Aux[A, AR]) {
def to[B] = new {
def apply[BR](f: AR => BR)(implicit reprB: LabelledGeneric.Aux[B, BR]): B =
reprB.from(f(reprA.to(a)))
}
}
Then we can use it like:
val a = Foo(1, "hello")
val b = Morph(a).to[RichFoo](_ + ('z ->> 0.9)) // => RichFoo(1,hello,0.9)
We can also make it handle re-orderings of fields like this:
case class Morph[A, AR](a: A)(implicit reprA: LabelledGeneric.Aux[A, AR]) {
def to[B] = new {
def apply[BR <: HList, BR2 <: HList](f: AR => BR2)(implicit reprB: LabelledGeneric.Aux[B, BR], align: Align[BR2, BR]): B =
reprB.from(align(f(reprA.to(a))))
}
}

Lifting a function which takes implicit parameter using functor (Scalaz7)

Just started learning Scalaz. Here is my code
trait Monoid[A] {
def mappend(a1: A, a2: A): A
def mzero: A
}
object Monoid {
implicit val IntMonoid: Monoid[Int] = new Monoid[Int] {
def mappend(a1: Int, a2: Int): Int = a1 + a2
def mzero: Int = 0
}
implicit val StringMonoid: Monoid[String] = new Monoid[String] {
def mappend(a1: String, a2: String): String = a1 + a2
def mzero: String = ""
}
}
trait MonoidOp[A] {
val F: Monoid[A]
val value: A
def |+|(a2: A): A = F.mappend(value, a2)
}
object MonoidOp{
implicit def toMonoidOp[A: Monoid](a: A): MonoidOp[A] = new MonoidOp[A]{
val F = implicitly[Monoid[A]]
val value = a
}
}
I have defined a function (just for the sake of it)
def addXY[A: Monoid](x: A, y: A): A = x |+| y
I want to lift it so that it could be used using Containers like Option, List, etc. But when I do this
def addXYOptioned = Functor[Option].lift(addXY)
It says error: could not find implicit value for evidence parameter of type scalaz.Monoid[A]
def addOptioned = Functor[Option].lift(addXY)
How to lift such functions?
Your method addXY needs a Monoid[A] but there is no Monoid[A] in scope when used in addXYOptioned, so you also need to add the Monoid constraint to addXYOptioned.
The next problem is that Functor.lift only lifts a function A => B, but we can use Apply.lift2 to lift a function (A, B) => C.
Using the Monoid from Scalaz itself :
import scalaz._, Scalaz._
def addXY[A: Monoid](x: A, y: A): A = x |+| y
def addXYOptioned[A: Monoid] = Apply[Option].lift2(addXY[A] _)
We could generalize addXYOptioned to make it possible to lift addXY into any type constructor with an Apply instance :
def addXYApply[F[_]: Apply, A: Monoid] = Apply[F].lift2(addXY[A] _)
addXYApply[List, Int].apply(List(1,2), List(3,4))
// List[Int] = List(4, 5, 5, 6)
addXYApply[Option, Int].apply(1.some, 2.some)
// Option[Int] = Some(3)

Trouble with inferred types when using Shapeless

I'm having a bit of trouble getting the inferred types of some extension methods that use Shapeless to line up with what (I think that) they should be. I can assign the result of my expressions to a val with an explicit type after passing them through identity or by way of a val without an explicit type (aaa and bb below respectively), but not directly (c below).
Tested using Scala 2.10.3, Shapeless 1.2.4, Scalaz 7.0.6.
import shapeless._, Tuples._
import scalaz._, Scalaz._
trait FnOps[=>:[_, _], A, B] {
val F: Arrow[=>:]
val f: A =>: B
def &&&:[C](g: A =>: C): A =>: (C, B) =
F.combine(g, f)
}
trait FnProdOps[=>:[_, _], A, B <: Product] {
val F: Arrow[=>:]
val f: A =>: B
def &&&:[C, R <: HList](g: A =>: C)(implicit hlister: HListerAux[B, R], tupler: Tupler[C :: R]): A =>: tupler.Out =
F.mapsnd(F.combine(g, f)) {
case (c, b) => tupler(c +: hlister(b))
}
}
trait LowPriorityImplicits0 {
implicit def ToFnOps[=>:[_, _], A, B](fn: A =>: B)(implicit A: Arrow[=>:]): FnOps[=>:, A, B] =
new FnOps[=>:, A, B] {
val F = A
val f = fn
}
}
object Implicits extends LowPriorityImplicits0 {
implicit def ToFnProdOps[=>:[_, _], A, B <: Product](fn: A =>: B)(implicit A: Arrow[=>:]): FnProdOps[=>:, A, B] =
new FnProdOps[=>:, A, B] {
val F = A
val f = fn
}
}
import Implicits._
// These work
val a: Int => Int = _ * 2
val aa: Int => (Int, Int) = a &&&: a
val aaa: Int => (Int, Int, Int) = identity(a &&&: a &&&: a)
// This works too
val b = a &&&: a &&&: a
val bb: Int => (Int, Int, Int) = b
// This doesn't
val c: Int => (Int, Int, Int) = a &&&: a &&&: a
Is there a way to make assignments similar to c compile?