使用函数依赖来消除类型参数

时间:2015-07-16 20:54:10

标签: haskell parsec functional-dependencies

我正在尝试实现一个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

2 个答案:

答案 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 tStreamDep 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类型签名。