Ich frage mich, ob es eine Möglichkeit gibt, über eine scheinbare Inkonsistenz in Scala die Behandlung von Function1 und Function2..N zu patchen.Scala-Typ-System und die Eingabe von FunctionN
Für eine Function1
, sagen Int => String
, die Parameterliste (Int) zu Int nicht identisch ist (auch wenn die beiden isomorph sind), aber der Compiler die Eingabe als bare Int (siehe Code unten) schließen.
Für Function2..N
, sagen val f: (String, Int) => String = ??? Der Compiler wird keinen Typ für die Eingabeparameterliste ableiten. Insbesondere gibt es keine ParameterList [String, Int], obwohl sie isomorph zu Tupeln von (String, Int) und jedem anderen Wrapper ist, den Sie gerne in Strings und Ints einsetzen.
Meine erste Frage ist, gibt es einen Grund, warum es so ist (oder ist das etwas, das auf einer Liste von Scala Todos existiert)? Warum kann Function1 in Eingabe- und Ausgabetyp dekonstruiert werden, aber nicht in Funktion2, und möchte jemand das beheben?
Gibt es irgendwelche Arbeiten um. Insbesondere gibt es im folgenden Code eine Möglichkeit, um Invoke2 arbeiten zu lassen?
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"
}
}
Sind rufen Sie Kenntnis von 'Function2 # tupled' und' Function2 # Curry-Methoden? –
Ja, ich bin :-) Und auch, dass Tupled nicht auf Function1 existiert. Vermutlich liegt das daran, dass die Fähigkeit, den _putput _ (als den blanken Typ) von Function1 auszudrücken, nicht notwendig ist. Schlechte alte Funktion2..N fehlt das. In diesem Sinne fühlt sich Tupel wie ein Workaround an. Ich denke aber, dass es einen guten Grund für die Diskrepanz zwischen Funktion 1 und 2 in dieser Hinsicht gibt? –