14

我编写了一个类似于Data.Enumerator.List.map使 an与提供不同类型的 anIteratee兼容的函数。EnumeratorStream

import Data.Enumerator

test :: Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b
test f iter = go $$ iter
   where go (Continue k) = continue $
            \stream -> go $$ k (fmap f stream)
         go (Yield res _) = yield res EOF

如果我省略 的类型签名go,这将正常工作。但是,我想包括它,但我无法确定正确的签名应该是什么。我认为应该是这样的:

go :: Monad m => Step ai m b -> Iteratee ao m b

但这不起作用。
我需要一些关于为go.

4

2 回答 2

16

您可能无法go按原样给出类型签名。

这样做的原因是它使用了由test. 这意味着,在 内部go,标识符f具有某些特定但未知(ao -> ai)类型的类型和.aoai

类型变量通常只在引入它们的单一类型签名的范围内,因此当您给出go自己的类型签名时,ao就会ai出现新的多态类型,这当然会在尝试将它们与类似的名称,但来自test的签名的固定(和不可知的)类型。

最终的结果是不能go显式地写出类型,这不是很令人满意。为了解决这个问题,GHC 提供了 ScopedTypeVariables 扩展,它允许将类型签名中引入的变量引入where函数子句的范围内,等等。

请注意,如果您只使用该where子句为定义创建内部范围,并且不使用由外部函数的参数绑定的标识符,您可以在该where子句中编写类型签名,就像您可以为顶级绑定一样。如果您不想使用 GHC 扩展,您可以简单地将参数传入冗余。在这种情况下,这样的事情应该可以工作:

test :: Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b
test f iter = go f $$ iter
  where go :: Monad m => (ao -> ai) -> Step ai m b -> Iteratee ao m b
        go f (Continue k) = continue $
             \stream -> go f $$ k (fmap f stream)
        go _ (Yield res _) = yield res EOF
于 2011-08-03T15:32:16.477 回答
9

您可能需要该类型,但ScopedTypeVariables启用了扩展并在范围内使用来自test的类型签名的变量:

{-# LANGUAGE ScopedTypeVariables #-}
import Data.Enumerator
test :: forall m ai ao b. Monad m => (ao -> ai) -> Iteratee ai m b -> Iteratee ao m b
test f iter = go $$ iter
   where go :: Step ai m b -> Iteratee ao m b
         go (Continue k) = continue $
            \stream -> go $$ k (fmap f stream)
         go (Yield res _) = yield res EOF

有关详细信息,请参阅GHC 文档

于 2011-08-03T15:31:02.787 回答