Les Options sont nommés et les arguments par défaut comme l'huile et l'eau dans une API Scala?
Je suis en train de travailler sur un Scala (API Twilio pour, par la voie), où les opérations ont un assez grand nombre de paramètres et beaucoup de ces derniers ont des valeurs sensibles par défaut. Pour réduire la frappe et d'augmenter la convivialité, j'ai décidé d'utiliser des classes de cas avec des nommés et des arguments par défaut. Par exemple, pour les TwiML Rassembler verbe:
case class Gather(finishOnKey: Char = '#',
numDigits: Int = Integer.MAX_VALUE, //Infinite
callbackUrl: Option[String] = None,
timeout: Int = 5
) extends Verb
Le paramètre d'intérêt, ici, est callbackUrl. C'est le seul paramètre qui est vraiment facultatif dans le sens que si aucune valeur n'est fournie, aucune valeur ne sera appliquée (ce qui est parfaitement légal).
J'ai déclaré comme une option pour faire les monadique carte de routine avec elle sur la mise en œuvre de l'API, mais ce qui met un peu la charge supplémentaire sur l'API utilisateur:
Gather(numDigits = 4, callbackUrl = Some("http://xxx"))
//Should have been
Gather(numDigits = 4, callbackUrl = "http://xxx")
//Without the optional url, both cases are similar
Gather(numDigits = 4)
Aussi loin que je peux faire, il existe deux options (no pun intended) pour résoudre ce problème. Soit faire de l'API client importer une conversion implicite dans l'étendue:
implicit def string2Option(s: String) : Option[String] = Some(s)
Ou je peux redeclare le cas de la classe avec une valeur null par défaut et de le convertir à une option sur le côté de mise en œuvre:
case class Gather(finishOnKey: Char = '#',
numDigits: Int = Integer.MAX_VALUE,
callbackUrl: String = null,
timeout: Int = 5
) extends Verb
Mes questions sont les suivantes:
- Y a plus élégante des façons de résoudre mon cas particulier?
- Plus généralement: les arguments Nommés est une nouvelle fonctionnalité du langage (2.8). Pourrait-il s'avérer que les Options et nommé par défaut arguments sont comme l'huile et l'eau? 🙂
- Peut utiliser une valeur par défaut null être le meilleur choix dans ce cas?
- Merci pour beaucoup de bonnes réponses pour la question 1 et 3! Je vais trouver d'autres exemples d'utilisation les cas d'utilisation de l'API avant de décider d'utiliser Aaron Opt, tenir à l'Option ou tout simplement utiliser une valeur null par défaut. Dommage qu'il n'y sont pas évidents, mais - peut-être que nous avons besoin d'une nouvelle fonctionnalité pour faire le choix d'une évidence?
Vous devez vous connecter pour publier un commentaire.
Voici une autre solution, en partie inspiré par Chris répondre. Elle implique aussi un wrapper, mais l'emballage est transparent, vous n'avez qu'à définir une fois, et l'utilisateur de l'API n'a pas besoin d'importer toutes les conversions:
D'aborder plus largement la question du design, je ne pense pas que l'interaction entre les Options et nommé par défaut des paramètres est comme l'huile et l'eau, comme il pourrait sembler à première vue. Il y a une nette distinction entre un champ facultatif et l'autre avec une valeur par défaut. Un champ facultatif (c'est à dire un type
Option[T]
) pourrait jamais ont une valeur. Un champ avec une valeur par défaut, sur l'autre main, il suffit de ne pas exiger de sa valeur fournie en argument du constructeur. Ces deux notions sont donc orthogonaux, et il n'est pas surprenant qu'un champ peut être en option et ont une valeur par défaut.Cela dit, je pense qu'un argument raisonnable peut être faite pour l'utilisation de
Opt
plutôt queOption
pour de tels domaines, au-delà de sauvegarder le client saisie. Ainsi, l'API plus souple, dans le sens que vous pouvez remplacer uneT
argument avec unOpt[T]
argument (ou vice-versa) sans casser les appelants du constructeur[1].Comme pour l'utilisation d'un
null
valeur par défaut pour un champ public, je pense que c'est une mauvaise idée. "Vous" savez peut-être que vous vous attendez à unenull
, mais des clients qui accèdent à la zone peut pas. Même si le terrain est privé, à l'aide d'unnull
est d'avoir des ennuis sur la route quand d'autres développeurs ont à maintenir votre code. Tous les arguments habituels surnull
valeurs entrent en jeu ici, je ne pense pas que ce cas d'utilisation est tout à fait exceptionnel.[1] à Condition que vous retirez le option2opt de conversion de sorte que les appelants doivent passer un
T
chaque fois qu'unOpt[T]
est nécessaire.null
par défaut, mais j'aime vraiment cette solution.N'est pas auto-convertir en rien à une Option. À l'aide de ma réponse ici, je pense que vous pouvez le faire très bien, mais dans un typesafe façon.
Ensuite, vous pouvez l'appeler comme vous voulez: évidemment l'ajout de votre comportement méthodes pour
FallbackUrl
etNumDigits
tant que de besoin. Les principaux négatif ici, c'est que c'est une tonne de passe-partoutSome("url")
Personnellement, je pense que l'utilisation de 'null' comme valeur par défaut est parfaitement OK ici. L'utilisation de l'Option à la place de null, c'est quand vous voulez transmettre à vos clients que quelque chose ne peut pas être définie. Ainsi, une valeur de retour peut être déclaré Option[...], ou l'un des arguments de méthode pour des méthodes abstraites. Cela permet d'économiser le client à partir de la lecture de la documentation ou, plus probablement, d'obtenir des Npe parce que de ne pas réaliser quelque chose peut être null.
Dans votre cas, vous êtes conscient qu'une valeur null peut être là. Et si vous aimez Option de méthodes que nous venons de faire
val optionalFallbackUrl = Option(fallbackUrl)
au début de la méthode.Cependant, cette approche ne fonctionne que pour les types de AnyRef. Si vous souhaitez utiliser la même technique pour tout type d'argument (sans qu'il en résulte Entier.MAX_VALUE de remplacement pour les nuls), alors je pense que vous devriez aller avec l'une des autres réponses
Option
est d'obtenir un état où il n'y a pas de valeur. Mais un tel état existe déjà dansnull
, et vous avez à gérer cela de toute façon.null
valeur par défaut pour une classe de cas de terrain. Étant donné que le champ est automatiquement public, toutes les raisons habituelles pour éviternull
s sont applicables. Même à l'intérieur de la définition de la classe, ayant ce potentielnull
valeur est sujette à erreur quand vous facteur dans la maintenance à long terme.Gather(timeout = 10, _callbackUrl = "http://my.org")
. Alors que les champ est privé, ses nom fait toujours partie de l'API publique. En outre, le fait que le le champ public (callbackUrl
) est un autre nom de l'argument du constructeur (_callbackUrl
) est susceptible de conduire à la confusion.Je pense que tant que pas de support de la langue de Scala pour une vraie sorte de vide (explication ci-dessous) "type", à l'aide de
Option
est probablement la meilleure solution à long terme. Peut-être même pour tous les paramètres par défaut.Le problème, c'est que les personnes qui utilisent votre API sais que certains de vos arguments sont en défaut pourrait tout aussi bien manier comme facultatif. Donc, ils sont les déclarant comme
C'est joli et propre et ils peuvent simplement attendre et voir si jamais ils obtiennent toutes les informations pour remplir cette Option.
Quand, finalement, l'appel de votre API avec un argument par défaut, ils seront confrontés à un problème.
Je pense qu'il serait beaucoup plus facile ensuite pour ce faire
où la
*openOrVoid
voudrais juste être laissé de côté en cas deNone
. Mais ce n'est pas possible.Donc vous devriez vraiment penser à qui va utiliser votre API et comment ils sont susceptibles de les utiliser. Il se pourrait bien que vos utilisateurs utilisent déjà
Option
de stocker toutes les variables pour la simple raison qu'ils savent qu'ils sont en option à la fin...Par défaut des paramètres sont gentils, mais ils ont également compliquer les choses, surtout quand il y a déjà un
Option
type de autour de. Je pense qu'il y a une part de vérité dans votre deuxième question.Some(thingElse)
?Pourrait je viens d'argumenter en faveur de votre approche actuelle,
Some("callbackUrl")
? Il est tout de plus de 6 caractères pour l'utilisateur API de type, leur indique que le paramètre est optionnel, et sans doute fait de la mise en œuvre plus facile pour vous.Je pense que vous devriez mordre la balle et aller de l'avant avec
Option
. J'ai été confronté à ce problème avant, et en général elle s'en alla après remaniement. Parfois, il n'a pas, et j'ai vécu avec elle. Mais le fait est qu'un paramètre par défaut n'est pas une "option" paramètre -- c'est juste celui qui a une valeur par défaut.Je suis plutôt en faveur de Debilski de l' réponse.
J'ai également été surpris par cette. Pourquoi ne pas le généraliser à:
Aucune raison pour que ne pouvait pas juste être une partie de Predef?
Some(null)
jonchent votre programme, et que tout l'intérêt d'avoir unOption
est la sécurité du type (c'est à dire qu'il n'est pas interchangeable avec son contenu)