Scala的类型系统和FunctionN

时间:2017-11-09 23:09:11

标签: scala shapeless type-systems

我想知道是否有一种修补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"
  }
}

1 个答案:

答案 0 :(得分:1)

Int => StringFunction1[Int, String]的语法糖,(String, Int) => StringFunction2[String, Int, String]的语法糖,((String, Int)) => StringFunction1[(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