真的不知道这里发生了什么我不是Haskell的新人
我正在尝试调用一个多态函数,我在两个不同的类型上定义了这个函数,而编译器对第二次函数调用提出了抱怨,因为类型与我第一次调用时的类型不同。
main :: IO ()
main = do
appData <- initializeAppData
let log = getDebugPrint appData :: Show a => PrintLevel -> a -> IO ()
log Log "My Game"
log Debug appData
我收到以下错误
/home/lianne/game/my-game/app/Main.hs:12:15: error:
• Couldn't match type ‘AppData’ with ‘[Char]’
Expected: String
Actual: AppData
• In the second argument of ‘log’, namely ‘appData’
In a stmt of a 'do' block: log Debug appData
In the expression:
do appData <- initializeAppData
let log = ...
log Log "My Game"
log Debug appData
如果我切换最后两行,让它在“My Game”之前记录appData
,那么它会说预期的AppData,而实际的类型是String。
为了清楚起见,我添加了let上的类型定义,看看它是否会强制多态。getDebugPrint
函数也有一个类型定义。
我使用堆栈和ghc 8.8.1
是因为它是在do块中定义的吗?
1条答案
按热度按时间2vuwiymt1#
这是单态限制。
有关单态限制的详细讨论,请参阅它的优秀标准问题:What is the monomorphism restriction?
但或多或少它说:如果一个绑定没有定义任何语法参数(不管它的类型是否指示它有参数),并且没有显式的类型签名用于绑定,那么任何具有类型类约束的类型变量都必须被解析为单个类型(满足约束)。
let log = ...
是一个 * 语法上 * 简单的变量绑定,没有给参数显式命名。但是“无显式类型签名”规则呢?你在这里写的是你想要的类型
Show a => PrintLevel -> a -> IO ()
,那么有什么呢?实际上,您编写它的方式并没有为变量
log
提供类型签名。相反,您为表达式getDebugPrint appData
提供了类型注解,这是不一样的。log
可以被赋予任何类型,该类型是右侧类型的示例化,它不必完全相同。1因此,既然你没有给变量
log
本身指定一个类型,编译器就必须推断出一个类型,这样做的时候,编译器会应用单态限制,这意味着它 * 不能 * 推断出Show a => PrintLevel -> a -> IO ()
;由于类型变量a
受Show a
的约束,因此必须将其示例化为一个特定的具体类型。它查看您 * 使用 *log
的位置以选择一个类型,因此,如果你只在一个类型上使用它,一切都会工作得很好。但是第一个调用log Log "My Game"
要求它选择String
,第二个调用要求它选择AppData
,显然它选择String
,然后抱怨另一个调用不匹配;我不确定它是否总是根据第一次使用来选择,或者它是否是任意的,但这并不重要。修复它的最简单方法是将您编写的类型放入
log
的类型签名中,而不是表达式的类型注解中(这正是您对修复类型感兴趣的地方),如下所示:至于为什么你从来没有这个问题之前,你应该能够看到从我上面的描述,有几个因素排队这是一个问题:
1.如果您只在单一型别上使用系结,编译器会根据用法推断出正确的单态型别。
1.如果在多个类型中使用的类型变量没有类约束,则不适用单态限制。
1.如果在绑定中有显式语法参数,则不适用单态限制。
1.在GHCi中,默认情况下
NoMonomorphismRestriction
扩展是激活的(从GHC 8开始,IIRC),它禁用了单态限制。所以你在解释器中尝试的任何东西都不会有这个问题。所以很有可能这只是第一次这种编码模式一次触发了所有的条件。
1如果你认为这是愚蠢的,考虑一下这个:
如果你不能在右边用更通用的表达式定义一个特定类型的变量,那么这就无效了,因为
1
自然具有Num a => a
类型。