2016-11-06 10 views
3

Я изучаю Haskell, так что это, наверное, что-то довольно тривиальное, но я был бы признателен за некоторые указания о том, как его переписать и как это работает.Рефакторинг где статья

У меня следующий рабочий код (используемые пакеты: HTF, Parsec и 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 

Я добавляю новый тест, который почти равно где часть, так что я пытался реорганизовать это следующим образом:

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 

Что не работает (даже если я включаю функцию lang., Как это предлагает компилятор).

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' и' assertBool' находятся в [* HTF *] (http://hackage.haskell.org/package/HTF-0.13.1.0/docs/Test-Framework-HUnitWrapper.html), а '(|>)' - из [* потока *] (https://hackage.haskell.org/package/flow-1.0.7/docs/Flow .html) и является просто синонимом 'flip ($)' и '(&)'. (Упоминание не очень известных пакетов, которые вы используете, делает для более четких вопросов.) – duplode

+0

@duplode О, спасибо (я думал, что импорта было достаточно). Я знал, что * поток * не очень популярен среди Haskellers, но * HTF * не слишком? Есть ли какая-то лучшая альтернатива, которая может запускать все модульные тесты в модуле без шаблона? – monnef

+0

«Я думал, что импорта было достаточно» - их достаточно в том смысле, что они предоставляют достаточно информации для поиска и, в конечном счете, их поиска и соответствующих функций. Для читателей вопрос просто для удобства. (Мне нечего сказать ни в одном из пакетов, которые вы использовали.) – duplode

ответ

0

Как вы уже заметили, что проблема была return вы добавили:

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

Тип mapM_ является:

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

positive и negative являются функциями, которые уже имеют соответствующие типы быть передано в mapM, и поэтому, если вы хотите, чтобы pos_neg_case вернул их в список, вам не нужно ничего делать, кроме как обертывания в списке. return не является ключевым словом; это просто функция, которая вводит значение в монадический контекст. Если вам не нужно делать такую ​​инъекцию, вам не нужно return.

P.S .: Цитирование ответ:

Но я должен был угадать тип Parser, отверстие дает мне очень сложный штуковина Text.Parsec.Prim.ParsecT s() Data.Functor.Identity.Identity a -> [s -> IO()].

Это пример довольно распространенного шаблона. - это конструктор типов с довольно многими переменными типа, а Parser является синонимом типа для общего набора вариантов некоторых из этих переменных, что позволяет использовать более типичные подписи, которые не упоминают их явно. Если вы посмотрите на это в documentation (индекс помогает в таких случаях) или использовать :info в GHCi, вы обнаружите, что Parser просто означает, что ...

type Parser = Parsec String() 

... и Parsec, в свою очередь, является ...

type Parsec s u = ParsecT s u Identity 

... так что расширение Parser синонима дает ParsecT String() Identity, что и GHC говорил вам, когда вы ввели типа отверстие.

+0

Спасибо за объяснение :). – monnef

0

Я до сих пор не уверен, что эти монады, но я как-то работает (те _ аки typeholes помогли, не знали о них).

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) 

Но я должен был угадать тип Parser, отверстие дает мне очень сложный штуковина - Text.Parsec.Prim.ParsecT s() Data.Functor.Identity.Identity a -> [s -> IO()].