我正在尝试实现一个Parsec Thu Jul 16 2015 15:53:22 GMT-0400 (Eastern Standard Time)
包装器,该包装器将记住最后一个Stream
标记,以便提供一些后视功能。我希望包装器可以与任何uncons
实例一起使用。这就是我到目前为止所拥有的:
Stream
这样可行,但我不想在{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module MStream where
import Text.Parsec
import Control.Monad ( liftM )
data MStream s t = MStream (Maybe t) s
instance Stream s m t => Stream (MStream s t) m t where
uncons (MStream _ s) = fmap (\(t, s') -> (t, MStream (Just t) s')) `liftM` uncons s
getPrevToken :: Stream s m t => ParsecT (MStream s t) u m (Maybe t)
getPrevToken = (\(MStream t _) -> t) `liftM` getInput
mstream :: s -> MStream s t
mstream = MStream Nothing
类型构造函数中携带t
参数。当然,仅要求MStream
参数就足够了,因为s
只要有t
的证人就可以从s
派生。我尝试过使用类型系列和GADT,但是我一直遇到关于模糊类型变量和不满意的函数依赖性的模糊错误。
有没有办法从Stream s m t
类型的构造函数中删除t
,所以我不必写:
MStream
答案 0 :(得分:1)
使用
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module MStream where
import Control.Monad ( liftM )
-- Class so we don't need to carry `m` in `MStream` definition.
class StreamDep s t | s -> t where
class StreamDep s t => Stream s m t where
uncons :: s -> m (Maybe (t, s))
data MStream s = forall t. StreamDep s t => MStream (Maybe t) s
data ParsecT s u m a = ParsecT s u (m a)
instance Monad m => Monad (ParsecT s u m) where
getInput :: ParsecT s u m s
getInput = undefined
instance StreamDep s t => StreamDep (MStream s) t where
instance (Monad m, Stream s m t) => Stream (MStream s) m t where
uncons (MStream _ s) = fmap (\(t, s') -> (t, MStream (Just t) s')) `liftM` uncons s
getPrevToken :: (Monad m, Stream s m t) => ParsecT (MStream s) u m (Maybe t)
getPrevToken = (\(MStream t _) -> t) `liftM` getInput
mstream :: StreamDep s t => s -> MStream s
我非常接近,但收到错误:
Pars.hs:28:35:
Could not deduce (t1 ~ t)
from the context (Monad m, Stream s m t)
bound by the type signature for
getPrevToken :: (Monad m, Stream s m t) =>
ParsecT (MStream s) u m (Maybe t)
at Pars.hs:27:17-76
or from (StreamDep s t1)
bound by a pattern with constructor
MStream :: forall s t. StreamDep s t => Maybe t -> s -> MStream s,
in a lambda abstraction
然而,通过使用上下文Stream s m t
和StreamDep s t1
,显而易见的是(t ~ t1)
。
通过使用火炮,我们可以编译:
getPrevToken :: (Monad m, Stream s m t) => ParsecT (MStream s) u m (Maybe t)
getPrevToken = (\(MStream t _) -> unsafeCoerce t) `liftM` getInput
但我不能尝试这个,因为它需要修改parsec
。
答案 1 :(得分:0)
所以,我通过移动球门柱(有点)来解决这个问题:
{-# LANGUAGE FlexibleContexts, FlexibleInstances,
MultiParamTypeClasses, FunctionalDependencies #-}
module MStream where
import Text.Parsec
import Control.Monad ( liftM )
class Stream s m t => MStream s m t | s -> t where
getPrevToken :: ParsecT s u m (Maybe t)
data MStreamImpl s t = MStreamImpl (Maybe t) s
instance Stream s m t => MStream (MStreamImpl s t) m t where
getPrevToken = (\(MStreamImpl t _) -> t) `liftM` getInput
instance Stream s m t => Stream (MStreamImpl s t) m t where
uncons (MStreamImpl _ s) = fmap (\(t, s') -> (t, MStreamImpl (Just t) s')) `liftM` uncons s
mstream :: s -> MStreamImpl s t
mstream = MStreamImpl Nothing
sillyParser :: MStream s m Char => ParsecT s u m String
sillyParser = do
t <- getPrevToken
maybe (string "first") (\c -> string $ "last" ++ [c]) t
我没有尝试从MStream
中删除类型参数,而是将MStream
转换为具有规范实例MStreamImpl
的类型类。现在,只需将sillyParser
上下文替换为Stream
上下文,即可以更紧凑的方式编写MStream
类型签名。