Haskell 类型错误 `map (\idx -> (2.400 ** idx) / fact idx) [0..9]` `fact :: Int -> Int`,预期 Int,实际 Double

Haskell type error `map (\idx -> (2.400 ** idx) / fact idx) [0..9]` `fact :: Int -> Int`, Expected Int, Actual Double

在解决下面这个问题时,我遇到了一个奇怪的错误。

module Main where

fact :: Int -> Int
fact 0 = 1
fact n = foldr (*) 1 [1..n]

calc_e_to_power :: Double -> Double
calc_e_to_power x = foldr (+) 0 $ map (\idx -> (x ** idx) / fact idx) [0..9]

main :: IO ()
main = do
  putStrLn $ show $ calc_e_to_power 2.4000

我收到 fact 函数的类型错误,它说期望 Int,得到 Double。这里idx的类型怎么可能是Double。

我知道 ** 会将第一个除法表达式的类型转换为 Double,但是 2.3243 / 3 在 ghci 中工作得很好。当我删除 fact 的类型签名时,它符合要求并完美运行。

不太确定我在这里遗漏了什么。

我还在 Repl 中重新创建了示例。有人可以帮助我了解这里出了什么问题吗?

Link 到 repl 上的代码 https://replit.com/@VipulSharma12/HeavyGrandioseCrypto#src/Main.hs

在你的表达中:

map (\idx -> (x ** idx) / fact idx) [0..9]

fact 期望 Int 和 returns 一个 Int,因此 idx 应该是 Intfact idx也是一个Int。但这与 (**) 被定义为 (**) :: Floating a => a -> a -> a 需要两个相同类型的项目相冲突,这两个项目应该是 Floating 类型类的成员。

特别是因为 x 的类型是 Double,因此 x ** idx 也将是 Double,这需要 idxDouble。但是 idx 应该是 fact idxInt,一个项目不能同时是 IntDouble

我们可以做的是使用 (^) :: (Num a, Integral b) => a -> b -> a,它接受类型为 b 的项目作为第二个操作数,这样 bIntegral 类型类的成员,当 bInt.

时就是这种情况

现在分子 x ^ idx 的类型为 Double(因为 (^) 的类型为 (^) :: (Num a, Integral b) => a -> b -> afact idx 的类型为 Int。我们不能将 Double 除以 Int,因为 (/) :: Fractional a => a -> a -> a 要求两个操作数具有相同的类型,并且该类型应该是 Fractional 类型类的成员(并且Int 不是 此类型类的成员)。

我们可以使用 fromIntegral :: (Integral a, Num b) => a -> bIntegral 数字(IntIntegral 类型类的成员)转换为任何类型 b其中 bNum 类型类的成员(并且 Double 是该类型类的成员)。

因此我们可以修复表达式并将其重写为:

calc_e_to_power :: Double -> Double
calc_e_to_power x = foldr (+) 0 $ map (\idx -> (x <strong>^</strong> idx) / <strong>fromIntegral</strong> (fact idx)) [0..9]

我们可以使用 sum :: (Foldable f, Num a) => f a -> a 来避免用 foldr 编写那部分:

calc_e_to_power :: Double -> Double
calc_e_to_power x = <b>sum</b> (map (\idx -> (x <strong>^</strong> idx) / fromIntegral (fact idx)) [0..9])