如何将此解析器转换为应用程序的多变量解析器?

时间:2015-09-12 03:44:05

标签: 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)

我预感到某种zipWithN会概括listUncurry。也许某种foldl ($)

作为一个附带问题(出于好​​奇心),可以生成parsec解析器吗?

2 个答案:

答案 0 :(得分:7)

实际上,您只需要Functor

listUncurry :: Functor f => (a -> a -> a -> r) -> f [a] -> f r
listUncurry h p =
  (\[x, y, z] -> h x y z) <$> p

对我来说,只有Functor是必要的提示就是你有这样的代码模式:

do x <- m
   return (f ...)

这相当于

m >>= (\x -> return (f ...))

相同
fmap (\x -> f ...) m

这是因为monad法律imply this identity

fmap f xs  =  xs >>= return . f

Polyvariadic listUncurry

我在大多数情况下都不建议这样做,因为它会将编译时错误转换为运行时错误,但这就是你实现多变量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"

如果您也使用它,则需要大量显式类型注释。可能有一种方法可以使用类型族或函数依赖来帮助解决这个问题,但我现在无法想到这一点。由于这可能是可解决的(至少在某种程度上),在我看来,更大的问题是类型错误从编译时错误变为运行时错误。

样本用法:

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

更安全listUncurry

如果您将课程更改为

class ListUncurry a x r where
  listUncurry :: a -> [x] -> Maybe r

并适当地更改实例中的错误情况,您至少会获得一个更好的界面来处理错误。如果您想保留该信息,也可以使用区分“太多”和“太少”参数错误的类型替换Maybe

虽然您需要添加更多错误处理(Maybe的{​​{1}},Functor和{{1},但我认为这会更好一些方法。接口将使这个相当不错。)

比较两种方法

最终取决于这将代表什么样的错误。如果程序执行不能再以任何有意义的方式继续,如果它遇到这样的错误,那么第一种方法(或类似的方法)可能比第二种方法更合适。如果有任何方式从错误中恢复,第二种方法将优于第一种方法。

首先应该使用多变量技术是一个不同的问题。重构程序可能会更好,以避免多变量材料的额外复杂性。

答案 1 :(得分:2)

  

我也确定我不应该oneElement列表 - 这样做的正确方法是什么?

otherElement的以下实施更有效:

snoc