2016-11-06 2 views
3

Je suis en train d'apprendre Haskell, donc c'est probablement quelque chose de plutôt trivial, mais j'apprécierais quelques conseils sur la façon de le réécrire et comment cela fonctionne.Refactoring où la clause

Je suit le code de travail (packages utilisés: HTF, Parsec et Flow):

{-# OPTIONS_GHC -F -pgmF htfpp #-} 
{-# LANGUAGE FlexibleContexts #-} 

module Main where 

import Test.Framework -- assertEqual, assertBool, htfMain, htf_thisModulesTests 
import Text.ParserCombinators.Parsec (eof, spaces, parse) 
import Flow ((|>)) 
import Data.Either (isLeft) 

whiteSpaces = spaces 

test_parse_whitespace = do 
    mapM_ positive [ 
     "", " ", "\t", "\n", "\r\n", " \r\n ", 
     " \t \r\n \t \n \r \t " 
    ] 
    mapM_ negative ["x", " x", "x ", " x ", "\t_\t"] 
    where 
    parser = whiteSpaces >> eof 
    parseIt = parse parser "" 
    positive str = assertEqual (parseIt str) (Right()) 
    negative str = assertBool (parseIt str |> isLeft) 

main :: IO() 
main = htfMain htf_thisModulesTests 

J'ajoute un nouveau test qui ont presque la même la où une partie, alors j'ai essayé de le refactoriser comme ceci:

pos_neg_case parser = do 
    return [positive, negative] 
    where 
    fullParser = parser >> eof 
    parseIt = parse fullParser "" 
    positive str = assertEqual (parseIt str) (Right()) 
    negative str = assertBool (parseIt str |> isLeft) 

test_parse_whitespace' = do 
    mapM_ positive [ 
     "", " ", "\t", "\n", "\r\n", " \r\n ", 
     " \t \r\n \t \n \r \t " 
    ] 
    mapM_ negative ["x", " x", "x ", " x ", "\t_\t"] 
    where 
    [positive, negative] = pos_neg_case whiteSpaces 

Ce qui ne fonctionne pas (même lorsque j'active la fonction lang. Comme le suggère le compilateur).

Couldn't match expected type ‘[Char] -> m b0’ 
      with actual type ‘[String -> IO()]’ 
Relevant bindings include 
    test_parse_whitespace' :: m() (bound at test/Spec.hs:21:1) 
In the first argument of ‘mapM_’, namely ‘positive’ 
In a stmt of a 'do' block: 
    mapM_ positive ["", " ", "\t", "\n", ....] 

Couldn't match expected type ‘[Char] -> m b1’ 
      with actual type ‘[String -> IO()]’ 
Relevant bindings include 
    test_parse_whitespace' :: m() (bound at test/Spec.hs:21:1) 
In the first argument of ‘mapM_’, namely ‘negative’ 
In a stmt of a 'do' block: 
    mapM_ negative ["x", " x", "x ", " x ", ....] 
+1

'assertEqual' et' assertBool' proviennent du [* HTF *] (http://hackage.haskell.org/package/HTF-0.13.1.0/docs/Test-Framework-HUnitWrapper.html), alors que '(|>)' provient de [* flux *] (https://hackage.haskell.org/package/flow-1.0.7/docs/Flow .html) et est juste un synonyme de 'flip ($)' et '(&)'. (Mentionner les paquets pas très connus que vous utilisez pour des questions plus claires.) – duplode

+0

@duplode Oh, merci (je pensais que les importations étaient suffisantes). Je savais que * flow * n'est pas très populaire parmi les Haskellers, mais * HTF * n'est pas aussi populaire? Y at-il une meilleure alternative qui peut exécuter tous les tests unitaires dans un module sans passe-partout? – monnef

+0

"Je pensais que les importations étaient suffisantes" - Elles sont suffisantes dans le sens où elles fournissent suffisamment d'informations pour les rechercher et éventuellement les trouver ainsi que les fonctions pertinentes. C'est juste une question de commodité pour les lecteurs de la question. (Je n'ai rien à dire contre l'un des paquets que vous avez utilisé.) – duplode

Répondre

0

Comme vous l'avez remarqué, le problème était le return vous avez ajouté à:

pos_neg_case parser = do 
    return [positive, negative] 
    where -- etc. 

Le type de mapM_ est:

GHCi> :t mapM_ 
mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m() 

positive et negative sont des fonctions qui ont déjà des types appropriés pour être passé à mapM, et donc si vous voulez pos_neg_case de les redonner sous forme de liste, vous n'avez rien d'autre à faire que d'envelopper dans une liste. return n'est pas un mot-clé; c'est juste une fonction qui injecte une valeur dans un contexte monadique. Si vous n'avez pas besoin de faire une telle injection, vous n'avez pas besoin de return.

P.S .: votre réponse Citant:

Mais je devais deviner le type de Parser, trou me donnait thingy très complexe Text.Parsec.Prim.ParsecT s() Data.Functor.Identity.Identity a -> [s -> IO()].

Ceci est un exemple d'un modèle assez commun. est un constructeur de type avec un certain nombre de variables de type, et Parser est un synonyme de type pour un ensemble commun de choix de certaines de ces variables, ce qui permet des signatures de type "rangé" qui ne les mentionnent pas explicitement. Si vous recherchez dans le documentation (l'indice aide beaucoup dans ce cas) ou utiliser :info dans GHCi, vous constaterez que Parser signifie juste ...

type Parser = Parsec String() 

... et Parsec, à son tour ...

type Parsec s u = ParsecT s u Identity 

... de sorte que l'élargissement du synonyme Parser donne ParsecT String() Identity, qui est ce que GHC vous dit quand vous avez introduit le trou de type.

+0

Merci pour l'explication :). – monnef

0

Je ne suis toujours pas sûr de ces monades, mais je l'ai eu de travail en quelque sorte (les _ aka typeholes beaucoup aidé, ne les connaissent pas).

pos_neg_case :: Parser a -> [String -> IO()] 
pos_neg_case parser = [positive, negative] 
    where 
    fullParser = parser >> eof 
    parseIt = parse fullParser "" 
    positive str = assertEqual (parseIt str) (Right()) 
    negative str = assertBool (parseIt str |> isLeft) 

Mais je devais deviner le type de Parser, trou me donnait thingy très complexe - Text.Parsec.Prim.ParsecT s() Data.Functor.Identity.Identity a -> [s -> IO()].