use*_*998 1 haskell parsec parser-combinators applicative polyvariadic
我正在尝试解析日期,例如09/10/2015 17:20:52:
{-# LANGUAGE FlexibleContexts #-}
import Text.Parsec
import Text.Parsec.String
import Text.Read
import Control.Applicative hiding (many, (<|>))
data Day = Day
{ mo :: Int
, dy :: Int
, yr :: Int
} deriving (Show)
data Time = Time
{ hr :: Int
, min :: Int
, sec :: Int
} deriving (Show)
day = listUncurry Day <$> (sepCount 3 (char '/') $ read <$> many digit)
time = listUncurry Time <$> (sepCount 3 (char ':') $ dign 2 )
dign :: (Stream s m Char, Read b) => Int -> ParsecT s u m b
dign = (read <$>) . flip count digit
-- how generalize to n?
listUncurry h [x1,x2,x3] = h x1 x2 x3
sepCount n sep p = (:) <$> p <*> (count (n-1) $ sep *> p)
Run Code Online (Sandbox Code Playgroud)
我有一种预感,某种zipWithN可以概括listUncurry.也许某种foldl ($)?
作为一个附带问题(出于好奇心),parsec解析器可以生成使用吗?
实际上,你只需要Functor:
listUncurry :: Functor f => (a -> a -> a -> r) -> f [a] -> f r
listUncurry h p =
(\[x, y, z] -> h x y z) <$> p
Run Code Online (Sandbox Code Playgroud)
对我来说,只有Functor在你有一个代码模式时才需要提示:
do x <- m
return (f ...)
Run Code Online (Sandbox Code Playgroud)
这相当于
m >>= (\x -> return (f ...))
Run Code Online (Sandbox Code Playgroud)
这是一样的
fmap (\x -> f ...) m
Run Code Online (Sandbox Code Playgroud)
这是因为monad法律暗示了这种身份:
fmap f xs = xs >>= return . f
Run Code Online (Sandbox Code Playgroud)
listUncurry我在大多数情况下并不是真的建议这样做,因为它会将编译时错误变成运行时错误,但这就是你如何实现polyvariadic listUncurry:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class ListUncurry a x r where
listUncurry :: a -> [x] -> r
instance ListUncurry k a r => ListUncurry (a -> k) a r where
listUncurry f (x:xs) = listUncurry (f x) xs
listUncurry _ _ = error "listUncurry: Too few arguments given"
instance ListUncurry r a r where
listUncurry r [] = r
listUncurry _ _ = error "listUncurry: Too many arguments given"
Run Code Online (Sandbox Code Playgroud)
如果您也使用它,则需要大量显式类型注释.可能有一种方法可以使用类型族或函数依赖来帮助解决这个问题,但我现在无法想到这一点.由于这可能是可解决的(至少在某种程度上),在我看来,更大的问题是类型错误从编译时错误变为运行时错误.
样品用法:
ghci> listUncurry ord ['a'] :: Int
97
ghci> listUncurry ((==) :: Int -> Int -> Bool) [1,5::Int] :: Bool
False
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a'] :: Bool
*** Exception: listUncurry: Too few arguments given
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a','b','c'] :: Bool
*** Exception: listUncurry: Too many arguments given
Run Code Online (Sandbox Code Playgroud)
listUncurry如果您将类更改为
class ListUncurry a x r where
listUncurry :: a -> [x] -> Maybe r
Run Code Online (Sandbox Code Playgroud)
并适当地更改实例中的错误情况,您至少会得到一个更好的界面来处理错误.Maybe如果要保留该信息,还可以使用区分"太多"和"太少"参数错误的类型替换它.
我觉得这将是一个方法的好一点,虽然你将需要添加更多的错误处理(Maybe的Functor,Applicative和Monad接口将使这相当不错,虽然).
它最终取决于这将代表什么样的错误.如果程序执行不能再继续在任何如果遇到这样的错误有意义的方式,那么第一种方法(或类似的方法)可能比第二种方法更合适.如果有任何方法可以从错误中恢复,第二种方法将优于第一种方法.
首先应该使用多变量技术是一个不同的问题.重构程序可能会更好,以避免多变量材料的额外复杂性.