在Haskell中使用有作用域的变量时会出现“Ambiguity type variable”?

ubof19bj  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(99)

OCaml代码:

for x = 0 to 12 do
    let i = (1 + (x * 3)) in
    let j = (60 - (x * 5)) in
    Printf.printf "I=%d J=%d\n" (i) (j);
done;;

我把它翻译成Haskell,但它不起作用:

import Text.Printf

for list action = mapM_ action list

main :: IO ()
main = do
    for [0..12] $ \x -> do
        let i = (1 + (x * 3))
            j = (60 - (x * 5)) in
            printf "I=%d J=%d\n" i j :: IO ()

第一个错误是:
从算术序列“0. 12“阻止求解约束”(Enum t0)“。
所以我必须将循环中的代码移动到一个新的函数中,然后它就可以工作了:

import Text.Printf

for list action = mapM_ action list

block :: Int -> IO ()
block x = 
    let i = 1 + (x * 3)
        j = 60 - (x * 5) in
        printf "I=%d J=%d\n" i j :: IO ()

main :: IO ()
main = do
    for [0..12] $ \x -> do block x

所以我的问题是Haskell允许在内部作用域中声明变量吗?如果是的话,我错过了什么?
在replit.com上测试

2nbm6dog

2nbm6dog1#

在这个片段中:

main :: IO ()
main = do
    for [0..12] $ \x -> do
        let i = (1 + (x * 3))
            j = (60 - (x * 5)) in
            printf "I=%d J=%d\n" i j :: IO ()

这里没有明确指出使用的是哪种数值类型。这将检查是否为012等。是Int s,Integer s,Word64 s,Double s或Complex
在某些有限的情况下,GHC会将未知的数值类型“默认”为IntegerDouble,这取决于它的使用方式,但这依赖于未知类型被约束到一组有限的“内置”类型类,而printf的使用引入了一个额外的类型类约束,干扰了这个默认过程。如果避免使用printf,则默认类型将按预期工作,因此以下工作(数字类型默认为Integer):

import Text.Printf

for list action = mapM_ action list

main :: IO ()
main = do
    for [0..12] $ \x -> do
        let i = (1 + (x * 3))
            j = (60 - (x * 5)) in
            putStrLn $ "I=" ++ show i ++ " J=" ++ show j

或者,如果你添加一个类型签名来显式地指示你想要使用的数值类型,它也可以工作:

import Text.Printf

for list action = mapM_ action list

main :: IO ()
main = do
    for [(0::Int)..12] $ \x -> do
    --     ^^^^^
        let i = (1 + (x * 3))
            j = (60 - (x * 5)) in
            printf "I=%d J=%d\n" i j :: IO ()

你也可以在let语句中给予类型签名,所以这也可以工作:

import Text.Printf

for list action = mapM_ action list

main :: IO ()
main = do
    for [0..12] $ \x -> do
        let i, j :: Int
        --  ^^^^^^^^^^^
            i = (1 + (x * 3))
            j = (60 - (x * 5)) in
            printf "I=%d J=%d\n" i j :: IO ()

基本上,任何让GHC找出类型的东西都会修复这个错误。您使用block的版本能够正常工作的原因是block具有将数值类型确定为Int的类型签名。如果你忽略了这个类型签名,你会得到同样的错误:

import Text.Printf

for list action = mapM_ action list

-- comment out the type signature, and it fails
-- block :: Int -> IO ()
block x = 
    let i = 1 + (x * 3)
        j = 60 - (x * 5) in
        printf "I=%d J=%d\n" i j :: IO ()

main :: IO ()
main = do
    for [0..12] $ \x -> do block x

相关问题