2017-11-09 24 views
2

ScalaがFunction1とFunction2..Nを処理する際に明らかな矛盾がある場合にパッチを当てる方法があるかどうかは疑問です。 Function1についてScalaの型システムとFunctionNへの入力

は、Int => Stringを言う、パラメータリスト(INT)が(二つの同型であっても)がINTと同一ではない、まだコンパイラは裸INT(以下のコードを参照)として入力を推測します。

Function2..Nの場合、 val f:(String、Int)=> String = ??? コンパイラは入力パラメータリストの型を推論しません。特に、(String、Int)のタプルおよびStringsとIntsを置くことを好む他のラッパーと同形であっても、ParameterList [String、Int]はありません。

私の最初の質問は、これがこのような理由があるかどうかです(またはこれはスカラートードのリストに存在します)。すなわち、なぜ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

invoke2を呼び出すことができFnToProductインスタンスを解決するための型崩れを助けることができますが、 '機能2番号のtupled'と'機能2番号を認識していますカレーの方法? –

+0

はい、私は:-)です。また、関数1にはタプルが存在しません。おそらくこれは、Function1の_input_(裸のタイプ)のタイプを表現する能力は、タプルが必要でないことを意味するからです。悪い古いFunction2..Nこれは欠けている。この意味で、タプルは回避策のように感じます。私は何を疑問に思いますか?この点については、Function1とFunction2の間に相違がある理由はありますか? –

答えて

1

Int => String((String, Int)) => StringFunction1[Tuple2[String, Int], String]別名Function1[(String, Int), String]の構文糖である、(String, Int) => StringFunction2[String, Int, String]の構文糖である、Function1[Int, String]の構文糖です。

あなたは暗黙の型変換

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)) 
    } 

を定義するなら、あなたは.tupled

val f: (String, Int) => String = (s, i) => s"I got $s and $i" 

val l = "s" :: 1 :: HNil 

val r2: String = invoke2(f.tupled, l) 

r2 == "I got s and 1" //true 
関連する問題