J'ai une méthode qui reçoit une fonction, mais cette fonction peut être partielle, dans ce cas je ne veux pas qu'elle échoue avec MatchError.
doSoemthing(x){
case t=> otherMethod(t)
}
De cette façon, je peux utiliser le méthode comme celle-ci
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
donc au cas où je n'ai pas de prédicat, je peux l'utiliser comme
this doSomething (x) (otherMethod) au lieu de
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p:PartialFunction[X,Y]=> opt.flatMap(p.lift) //This doesn't seem to work
case _ => opt.map(f)
}
}
Remarque: vous recherchez une solution qui ne nécessite pas de capturer MatchError code> exceptions
3 Réponses :
Je ne suis pas sûr de ce que vous passez en tant que fonction partielle, mais vous devriez certainement la définir avec une signature spécifique comme celle-ci:
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p if p.isInstanceOf[PartialFunction[X,Y]] =>
println("I'm a pf")
println(s"Is it PartialFunction: ${p.isInstanceOf[PartialFunction[X,Y]]}")
opt.map(p)
case _ =>
println("I'm not a pf")
opt.map(f)
}
}
doSomething[Int, Option[Int]](Some(5))(positive) // partial function case
doSomething[Int, String](Some(5)) { // tricky case
case s => s.toString
}
Le code positif > La fonction est définie uniquement pour les nombres positifs. En cas de nombres négatifs, la fonction renvoie None et vous n'obtiendrez pas scala.MatchError à l'exécution.
Cette fonction spécifique vous permet d'accéder à la méthode isDefinedAt qui teste dynamiquement si une valeur est dans le domaine de la fonction.
postive (5) .isDefinedAt // true
poistive.isInstanceOf [PartialFunction [Int, Option [Int]]] // true
J'ai démontré ici pourquoi vous obtenez toujours false lorsque vous vérifiez p.isInstanceOf
val positive: PartialFunction[Int, Option[Int]] = {
case x if x >= 0 => Some(x)
case _ => None
Vous pouvez jouer avec ici:
Pourquoi un vote négatif pourriez-vous s'il vous plaît expliquer la raison, si je comprends bien la question, comment pouvons-nous utiliser correctement isInstanceOf [PartialFunction [X, Y]].
Ceci n'est pas la question.
Cependant, ma réponse couvre les deux côtés, lisez les commentaires de l'auteur ci-dessous la question. Mais lisez le titre de la question!
1. Une fonction partielle qui renvoie des options est une redondance. 2. Si j'inline positif , votre exemple échoue. 3. J'essaye de supprimer le passe-partout (comme vu dans mon dernier exemple), donc extraire le lambda et ajouter des annotations de type explicite va à l'encontre de tout le but. 4. Je n'essaye pas de comprendre comment les PartialFunctions fonctionnent, mais j'essaie de trouver un moyen d'insérer une fonction partielle et de faire en sorte que le compilateur compile réellement une fonction partielle et non une "fonction totale" qui échoue sur certaines entrées.
Ce n'est pas une réponse car je ne pense pas que ce que vous voulez soit possible dans Scala.
La méthode originale est correcte et fonctionne comme prévu, même si elle pourrait être un peu plus simple:
XXX
Le problème est ici:
x.collect{
case ...
}
Scala crée une Function plutôt qu'une PartialFunction de cette expression match donc le test échoue. Si vous passez une vraie PartialFunction , la méthode fonctionne bien.
x.map(f)
Je ne pense pas qu'il y ait un moyen de faire ce que vous voulez, principalement parce que doSomething a plusieurs listes d'arguments qui gâchent la déduction de type pour la deuxième liste d'arguments.
Ma suggestion est simplement d'utiliser
val p: PartialFunction[Int, Int] = {
case i: Int if i > 0 => i
}
doSomething(Some(0))(p) // Returns None
ou
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
selon le cas dans le code d'appel.
La syntaxe de la fonction partielle a été modifiée depuis 2.9 par SLS 8.5, de sorte que même si vous faites {case x => y} , cela NE signifie PAS que c'est une fonction partielle. Son type sera exact tel que vous le définissez comme.
Dans votre cas, vous l'avez défini comme X => Y (comme dans votre paramètre de fonction), il ne s'agit donc que d'un X => Y (il a été compilé dans une fonction régulière, et les cas de non-correspondance lèveront MatchError), et même vous faites isInstanceOf [PartialFunciton [_, _]] , ce ne sera pas le cas match.
Pour que votre scénario fonctionne, vous pouvez simplement convertir la fonction passée en PartialFunction, comme:
doSomething(Some(1)){case 2 => 0} //This gives MatchError and it is not recognized as PartialFunction inside the body
tandis que
doSomething(Some(1))({case 2 => 0}: PartialFunction[Int,Int]) //This returns None without MatchError
Ce n'est probablement pas aussi pratique que vous le pensiez, mais c'est la seule façon de le faire fonctionner. (ou vous définissez 2 fonctions distinctes pour les deux cas, comme collect et map dans la bibliothèque standard)
Pourquoi ne pas toujours recevoir une
X => Option [Y]?D'un autre côté, vous pouvez faire quelque chose de sale comme
if (p.is InstanceOf [PartialFunction [X, Y]]) opt.flatMap (p.asInstanceOf [PartialFunction [X, Y]]. Lift) code >J'ai déjà essayé ça. Cela ne fonctionne tout simplement pas. Si le compilateur attend une fonction totale, peu importe qu'elle soit écrite comme une fonction partielle, il compilera une fonction totale, donc
.isInstanceOf [PartialFunction [X, Y]]renvoie toujours false.Pourquoi ne pas aller dans l'autre sens et n'accepter que des fonctions partielles:
def doSomething [X, Y] (opt: Option [X]) (p: PartialFunction [X, Y]) = opt.flatMap (p.lift) < / code>? Pour "au cas où je n'ai pas de prédicat", vous pouvez écriredoSomething (x) (otherMethod (_)).Wow, je ne savais pas que c'était possible
@AlexeyRomanov, en effet, cela ne fonctionne pas.
Vous avez raison, j'aurais dû le tester. Je me suis mal souvenu des règles pertinentes.