作为Haskell的原始初学者,我试图在ghc编译器中使用-Wall选项时,不加警告地编译所有练习代码。我也试图理解'$'和'。'的使用。避免过多的括号。
在下面的代码中
module Helpers (intSqrt1, intSqrt2) where
intSqrt1 :: Int -> Int
intSqrt1 x = truncate $ sqrt $ fromIntegral x
intSqrt2 :: Int -> Int
intSqrt2 x = truncate ( sqrt (fromIntegral x) :: Double)
Run Code Online (Sandbox Code Playgroud)
intSqrt1发出警告,将以下约束默认设置为类型'Double'。我可以通过将结果从sqrt强制为Double来抑制警告(请参阅intSqrt2),但是这样做的代价是添加两对括号。
有没有办法在此功能中兼得两全:即简洁的代码和警告的抑制?
bra*_*drn 10
这里发生的是,您正在使用fromIntegral将转换Int为某种类型a,正在使用sqrt转换a为a,然后使用truncate转换a回Int。根据这些功能的约束,GHC知道a必须为Floatingand RealFrac,但不知道其a为。为了解决这个问题,GHC维护了一组默认规则;在这种情况下,它们声明任何为Floating或RealFrac默认为的模糊类型Double。但是,默认情况并非在所有情况下都是预期的行为,因此GHC还会打印警告。
当您添加类型签名时,歧义将被删除,这就是消息消失的原因。但是添加类型签名有点麻烦。有没有更好的办法?其实有!首先,您需要TypeApplications通过在文件顶部放置以下编译指示来启用扩展名:
{-# LANGUAGE TypeApplications #-}
Run Code Online (Sandbox Code Playgroud)
通过此扩展,您可以将语法@SomeType用作任何函数的第一个参数。如果函数的签名中包含任何类型变量,则将第一个专门用于SomeType。(后续用途专门用于第二,第三,第四等类型变量。)在这种情况下,我们可以选择放置类型应用程序的位置。我们可以穿上它fromIntegral:
{-# LANGUAGE TypeApplications #-}
Run Code Online (Sandbox Code Playgroud)
(请注意,它fromIntegral具有两个类型变量,因此我们将第一个变量推断为,Int而仅将第二个变量专门化。)
或者我们可以穿上它sqrt:
intSqrt x = truncate $ sqrt $ fromIntegral @_ @Double x
Run Code Online (Sandbox Code Playgroud)
或在truncate:
intSqrt x = truncate $ sqrt @Double $ fromIntegral x
Run Code Online (Sandbox Code Playgroud)
这些变体中的任何一个都可以简洁地解决该问题。