haskell 对do块中的两个不同类型调用多态函数时出现类型错误

balp4ylt  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(164)

真的不知道这里发生了什么我不是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块中定义的吗?

2vuwiymt

2vuwiymt1#

这是单态限制。
有关单态限制的详细讨论,请参阅它的优秀标准问题:What is the monomorphism restriction?
但或多或少它说:如果一个绑定没有定义任何语法参数(不管它的类型是否指示它有参数),并且没有显式的类型签名用于绑定,那么任何具有类型类约束的类型变量都必须被解析为单个类型(满足约束)。

let log = getDebugPrint appData :: Show a => PrintLevel -> a -> IO ()

let log = ...是一个 * 语法上 * 简单的变量绑定,没有给参数显式命名。
但是“无显式类型签名”规则呢?你在这里写的是你想要的类型Show a => PrintLevel -> a -> IO (),那么有什么呢?
实际上,您编写它的方式并没有为变量log提供类型签名。相反,您为表达式getDebugPrint appData提供了类型注解,这是不一样的。log可以被赋予任何类型,该类型是右侧类型的示例化,它不必完全相同。1
因此,既然你没有给变量log本身指定一个类型,编译器就必须推断出一个类型,这样做的时候,编译器会应用单态限制,这意味着它 * 不能 * 推断出Show a => PrintLevel -> a -> IO ();由于类型变量aShow a的约束,因此必须将其示例化为一个特定的具体类型。它查看您 * 使用 * log的位置以选择一个类型,因此,如果你只在一个类型上使用它,一切都会工作得很好。但是第一个调用log Log "My Game"要求它选择String,第二个调用要求它选择AppData,显然它选择String,然后抱怨另一个调用不匹配;我不确定它是否总是根据第一次使用来选择,或者它是否是任意的,但这并不重要。
修复它的最简单方法是将您编写的类型放入log的类型签名中,而不是表达式的类型注解中(这正是您对修复类型感兴趣的地方),如下所示:

main :: IO ()
main = do
    appData <- initializeAppData
    let log :: Show a => PrintLevel -> a -> IO ()
        log = getDebugPrint appData
    log Log "My Game"
    log Debug appData

至于为什么你从来没有这个问题之前,你应该能够看到从我上面的描述,有几个因素排队这是一个问题:
1.如果您只在单一型别上使用系结,编译器会根据用法推断出正确的单态型别。
1.如果在多个类型中使用的类型变量没有类约束,则不适用单态限制。
1.如果在绑定中有显式语法参数,则不适用单态限制。
1.在GHCi中,默认情况下NoMonomorphismRestriction扩展是激活的(从GHC 8开始,IIRC),它禁用了单态限制。所以你在解释器中尝试的任何东西都不会有这个问题。
所以很有可能这只是第一次这种编码模式一次触发了所有的条件。
1如果你认为这是愚蠢的,考虑一下这个:

x :: Int
x = 1

如果你不能在右边用更通用的表达式定义一个特定类型的变量,那么这就无效了,因为1自然具有Num a => a类型。

相关问题