我想知道是否有一种修补Scala处理Function1和Function2..N的明显不一致的方法。
对于Function1
,比如Int => String
,参数列表(Int)与Int不相同(即使两者是同构的),但编译器会推断输入为裸Int(参见下面的代码)。
对于Function2..N
,请说
val f:(String,Int)=> String = ???
编译器不会推断输入参数列表的任何类型。特别是,没有ParameterList [String,Int],即使它与(String,Int)的元组和你喜欢的字符串和Ints的任何其他包装器是同构的。
我的第一个问题是,是否有这样的原因(或者这是scala todos列表中存在的东西)?即为什么可以将Function1解构为输入和输出类型而不是Function2,是否有人想解决这个问题?
有没有解决方法。具体来说,在下面的代码中,有没有办法使invoke2工作?
package net.jtownson.swakka
import org.scalatest.FlatSpec
import org.scalatest.Matchers._
import shapeless.ops.function._
import shapeless.{HList, HNil, _}
class TypeclassOfFunctionTypeSpec extends FlatSpec {
// Here, we know the return type of F is constrained to be O
// (because that's how the shapeless FnToProduct typeclass works)
def invoke1[F, I <: HList, O](f: F, i: I)
(implicit ftp: FnToProduct.Aux[F, I => O]): O = ftp(f)(i)
// So let's try to express that by extracting the input type of F as FI
def invoke2[FI, I <: HList, O](f: FI => O, i: I)
(implicit ftp: FnToProduct.Aux[FI => O, I => O]): O = ftp(f)(i)
"Invoke" should "work for a Function1" in {
// Here's our function (Int) => String
val f: (Int) => String = (i) => s"I got $i"
val l = 1 :: HNil
// this works
val r1: String = invoke1(f, l)
// So does this. (With evidence that the compiler sees the function parameter list (Int) as just Int
val r2: String = invoke2[Int, Int::HNil, String](f, l)
r1 shouldBe "I got 1"
r2 shouldBe "I got 1"
}
"Invoke" should "work for a Function2" in {
// Here's our function (String, Int) => String
val f: (String, Int) => String = (s, i) => s"I got $s and $i"
val l = "s" :: 1 :: HNil
// this works
val r1: String = invoke1(f, l)
// But this does not compile. There is no expansion for the type of FI
// (String, Int) != the function Parameter list (String, Int)
val r2: String = invoke2(f, l)
/*
Error:(...) type mismatch;
found : (String, Int) => String
required: ? => String
val r1: String = invoke1(f, l)
*/
r1 shouldBe "I got s and 1"
r2 shouldBe "I got s and 1"
}
}
答案 0 :(得分:1)
Int => String
是Function1[Int, String]
的语法糖,(String, Int) => String
是Function2[String, Int, String]
的语法糖,((String, Int)) => String
是Function1[(String, Int), String]
aka {{的语法糖1}}。
如果您定义隐式转换
,则可以帮助Shapeless解析Function1[Tuple2[String, Int], String]
个实例
FnToProduct
然后,您可以使用implicit def tupledFnToProduct[FI1, FI2, O, Out0](implicit
ftp: FnToProduct.Aux[Function2[FI1, FI2, O], Out0]
): FnToProduct.Aux[Function1[(FI1, FI2), O], Out0] =
new FnToProduct[Function1[(FI1, FI2), O]] {
override type Out = Out0
override def apply(f: Function1[(FI1, FI2), O]) = ftp((x, y) => f(x, y))
}
invoke2
.tupled