假设我们有以下newtype
定义:
newtype A = A { _run :: Monad m => A -> [Int] -> m Int }
这不能用GHC 8.0.2编译:
error: Not in scope: type variable ‘m’
使用像m
或IO
这样的具体类型类替换[]
确实可以编译,正如我所期望的那样。鉴于这没关系,为什么GHC不允许上面的签名?在newtype
内添加类型类约束有什么问题?
答案 0 :(得分:8)
这取决于您在A
中尝试存储的内容。
如果您尝试存储此类任何功能,只要m
为Monad
,请将其用作类型参数,并在您的函数中指定此约束:
newtype A m = A { _run :: A m -> [Int] -> m Int }
myFunction :: Monad m => A m -> A m
然后,您可以在构造函数中包含A [] -> [Int] -> [Int]
或A Maybe -> [Int] -> Maybe Int
等内容。
f :: A Maybe -> [Int] -> Maybe Int
f _ (x:_) = Just x
f _ [] = Nothing
g :: Monad m => A m -> [Int] -> m Int
g _ xs = return $ head xs
myA :: A Maybe
myA = A f -- this works
myOtherA :: Monad m => A m
myOtherA = A g -- this works too
另一方面,如果要强制存储的数据是多态的,可以使用GHC扩展RankNTypes
。
{-# LANGUAGE RankNTypes #-}
newtype A = A { _run :: forall m. Monad m => A -> [Int] -> m Int }
myFunction :: A -> A
你在构造函数中不能拥有A -> [Int] -> [Int]
或A -> [Int] -> Maybe Int
之类的内容,因为forall
强制它们优先于任何 {{ 1}},所以它必须是Monad m
类型。
Monad m => A -> [Int] -> Maybe Int
如果您打算为f :: A -> [Int] -> Maybe Int
f _ (x:_) = Just x
f _ [] = Nothing
g :: Monad m => A -> [Int] -> m Int
g _ xs = return $ head xs
myA :: A
myA = A f -- this does not work ; it wants forall m. Monad m => m, not []
myOtherA :: A
myOtherA = A g -- this does work
- 值使用不同的特定Monad
实例,这只会非常有用。例如,镜头通过使用不同的仿函数来对镜头做不同的事情。
答案 1 :(得分:5)
这是可能的:
{-# LANGUAGE RankNTypes #-}
newtype A = A { _run :: forall m. Monad m => A -> [Int] -> m Int }
很难说出你想做什么,但这并不是很有用。类型A
的任何值都需要适用于所有monad(您无法选择)。
这也是可能的,具有相同的限制:
{-# LANGUAGE GADTs #-}
data A where A :: Monad m => (A -> [Int] -> m Int) -> A
但也许你的意思更像是
newtype A m = A { _run :: A m -> [Int] -> m Int }
这允许使用不同monad的不同类型A
的值。
答案 2 :(得分:-1)
在创建Monad
类型的数据时,GHC如何知道要使用的A
实例?
或者换句话说,类型变量m
不在类型定义左侧的范围内。这意味着它不知道m
应该是什么,并且不能解决它。这是隐含的。
我确信你可以用扩展程序做一些你想做的事情,可能使用明确的forall。 (RankNTypes扩展名),但是我们需要知道你的内容。