8
votes

Devrait peut-être être utilisé pour contenir des messages d'erreur?

J'ai une fonction HASKELLL qui prend une entrée utilisateur et une autre fonction qui valide cette entrée. Bien sûr, la validation pourrait échouer, auquel cas je voudrais renvoyer un message d'erreur donnant des commentaires sur ce qui a été mal fait.

Je sais qu'il y a plusieurs façons que je puisse faire cela. Après la petite expérience que j'ai, il semble que le meilleur moyen soit d'utiliser soit une chaîne A . Ce qui me jette, c'est que je me fiche du a . Soit il échoue et j'aimerais stocker plus d'informations ou réussir. Le A est gaspillé.

utilise peut-être chaîne un moyen acceptable de stocker un message d'erreur? Ça me sent en arrière, mais ignore complètement la valeur à droite d'un est assez mauvais aussi. Qu'est-ce qui est Canonical ici?


3 commentaires

On dirait que le code que vous écrivez comprend des anticiperns (dans ce cas, éventuellement du code côté effecteur). Vous devriez généralement vous soucier de valeur d'un (pas de jeu de mots ou de confusion) - c'est-à-dire que dans les deux gauche et droit doit être traité de manière appropriée.


@Jules, je suis d'accord que je devrais m'occuper de chaque côté du soit , c'est pourquoi je pensais que l'utilisation de peut-être était la voie à suivre. Dites-vous que l'utilisateur de l'utilisateur est l'anticitatoire?


Sémantiquement votre fonction de validation peut renvoyer un message d'erreur ou renvoyer un message d'erreur, il semble donc tout à fait raisonnable d'utiliser peut-être pour son type de retour. Ce n'est pas le modèle "Message d'erreur" VS "Ressort correct" que soit implique généralement.


5 Réponses :


4
votes

Qu'est-ce qu'un message d'erreur et une valeur attendue n'est qu'une question de point de vue. Si vous ne vous souciez pas de la valeur de résultat , mais faites attention à un message d'erreur possible, ce message est, aussi loin que vous êtes concerné, la valeur d'intérêt. Donc, assurez-vous que vous pouvez le stocker comme un peut-être chaîne .

En fait, il est peu différent avec soit . Ce que je trouve en arrière, c'est que soit est généralement perçu comme "le type d'échec possible". Oui, son instance monad arrive à fonctionner dans la manière qui fait gauche error-ish et droite succès-ish, mais ab initio est juste Un simple bifoncteur exprimant la somme de deux types. En effet, si Haskell avait toujours eu des opérateurs de type, soit A B serait probablement écrit A + B ou A || b .

Si vous aviez un String a et souhaitez "confisquer" la valeur possible A , le moyen le plus simple est de simplement fmap (const ()) sur elle, entraînant un String () , qui est isomorphe à peut-être chaîne mais "ressemble plus à la chaîne a Caractère d'erreur ", bien que j'ai dit que ceci est un peu stupide.

Pour préciser les types que vous parlez de messages d'erreur, je n'utiliserais aucun ni ni peut-être mais sauf chaîne () . Souvent, les valeurs d'erreur sont prises sur une autre monad quand même, vous auriez donc par exemple. sauf String io () .


0 commentaires

4
votes

8 commentaires

Avec peut-être , les validateurs peuvent être chine trivialement avec <|> ; Avec ce type personnalisé, ce n'est pas le cas.


Je viens d'apprendre à propos d'une nouvelle classe de type, merci d'avoir apporté la note sur <|> . Serait-il raisonnable de générer une instance d'alternative pour obtenir ce comportement? Ou est-ce le genre de chose où si je le mettez moi-même, je le fais probablement mal?


@jcolemang Oui, ce serait raisonnable. Il est un peu malheureux d'avoir à dupliquer le code déjà présent. Je vais mettre à jour avec une autre alternative.


Combinant des validateurs avec <|> est un exemple parfait de pourquoi vous devriez définir un nouveau type ok | Erreur A , sauf si vous souhaitez penser à un validateur "succédant" quand il "réussit" trouve quelque chose d'invalide sur l'entrée.


@Reidbarton Si vous avez considéré un validateur réussissant quand il n'a pas trouvé quelque chose d'invalide, n'utiliserais pas <|> résultat dans un résultat valide si seulement des validateurs ont échoué? Ou suis-je mal compris?


Le point est toute la prémisse d'utiliser <|> pour les validateurs de chaîne (en supposant que cela signifie que vous souhaitez vérifier si elles acceptent l'entrée comme valide) est en cours et déroutant, car a <| > B consiste à réussir lorsque a ou B fait. D'où l'alternative du nom. La bonne façon de chaîaliser les validateurs est avec >> (ou quel que soit le nom branché de cet opérateur, est de nos jours). Cela fonctionne par exemple avec String () .


Ce type ne peut pas être un Foncteur , alternatif , applicatif ou monad , comme il a le mauvais type, alors Votre exemple avec GND est incorrect. Ce n'est pas si mauvais, cependant, puisque les cas n'auraient pas vraiment de sens - étant donné que Error Si le court-circuit, il n'y aurait aucun endroit pour contenir la valeur "Success" et aucun de l'habitude. les typlasses seraient utiles.


@Alexisking Oh, non! Merci. Maintenant, je ne crois plus que la deuxième approche est utile.



3
votes

oui, peut-être string code> est correct (sa forme de forme précisément sur la plage de votre fonction), mais elle ne compose pas bien car toutes les instances utiles prennent la sémantique opposée pour peut-être code>. String () code> serait plus utile (en termes de monad / instances applicatives) et serait également plus clair.

mais il y a une abstraction "de validation" plus appropriée "qui vaudrait la peine Explorer, ce qui vous permet de chaîner des validations et d'accumuler des erreurs (c'est-à-dire sans court-circuiter sur la première erreur). Certaines saveurs de mise en œuvre sont dans le validation code> package . Du Docs: P>

>>> _Success # (+1) <*> _Success # 7 :: AccValidation String Int
AccSuccess 8

>>> _Failure # ["f1"] <*> _Success # 7 :: AccValidation [String] Int
AccFailure ["f1"]

>>> _Success # (+1) <*> _Failure # ["f2"] :: AccValidation [String] Int
AccFailure ["f2"]

>>> _Failure # ["f1"] <*> _Failure # ["f2"] :: AccValidation [String] Int
AccFailure ["f1","f2"]


0 commentaires

10
votes

J'encourage l'utilisation de sauf chaîne () code> (ou String () code>) sur peut-être chaîne code>, pour quelques raisons:

  • Vous trouverez probablement plus tard que votre entreprise est pratique pour votre fonction de validation pour renvoyer certaines parties de la structure de données. Par exemple, lors de la validation d'une chaîne code> code> est un numéro de téléphone, vous pouvez d'abord vouloir renvoyer l'indicatif régional, d'abord et deux parties du numéro, donnant un type de validation tel que String -> Sauf Chaîne (int, int, int) code> ou similaire. Faire des validateurs qui ne retournent rien d'intéressant ont le type FOO -> Sauf String () Code> Les fait juste un cas particulier de ce motif - et donc plus facile à adapter ensemble. Li>
  • Continuer la partie "Ajuster ensemble", vous pouvez ensuite constater que vous souhaitez construire un grand validateur en plus petits. Vous avez peut-être un validateur qui vérifie qu'une personne a spécifié une date d'âge et de naissance valide et souhaitez créer un validateur en dehors de cela qui vérifie également que l'âge est à peu près bien à la date de naissance. L'instance monad code> pour soit code> aidera ici; Par exemple: p>

    validatePerson p now = do
        age <- validateAge p
        date <- validateBirthdate p
        validateMatchingAgeAndDate age date now
    


  • 4 commentaires

    Après avoir utilisé Haskell pendant quelques mois, je voudrais dire que je suis très fortement d'accord pour dire que c'est la voie à suivre.


    N'a-t-il pas d'utilisateur de chaîne être une meilleure option?


    @ MB14 Qu'est-ce que utilisateur , et pourquoi pensez-vous que ce serait une meilleure option que () ?


    J'ai raté et pensé que l'OP voulait valider un utilisateur ainsi une fonction validateuser :: Utilisateur -> Error string utilisateur . Est l'avantage d'être à la chaîne. Cependant, cela n'applique pas la fonction de validation pour ne pas modifier l'entrée.



    1
    votes

    Note: Je ne crois pas que c'est la meilleure solution en général, mais compte tenu de cela pour une mission que je ne travaillerai que sur et qui n'incluait aucune exigence pour le bon haskell Pratiques (ou HASKELL du tout), c'est la solution la plus facile pour moi. J'ai emprunté quelques idées de la réponse de Chi.


    je voulais la fonctionnalité exacte du peut-être , mais je voulais toujours éviter de retourner rien pour indiquer le succès et le code> juste msg pour indiquer échec. Pour contourner cela, j'ai créé ce que sont vraiment des alias vers peut-être et ses constructeurs de données: xxx

    ceci permet la fonctionnalité complète de peut-être mais le vérificateur de type affichera toujours maybefailure si vos types sont incorrects et que vous ne voyez pas peut-être chaîne dans la signature de type, ce qui serait trompeur. < / p>

    L'inconvénient est que ces deux fonctions fonctionnent toujours: xxx


    0 commentaires