作为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)
intSqrt1给出警告,默认以下约束类型为“ Double” 。我可以通过将结果从sqrt强制为Double来抑制警告(请参阅intSqrt2),但是这样做的代价是添加两对括号。
在此功能中是否有办法同时兼顾两个方面:即简洁的代码和警告的抑制?
答案 0 :(得分:10)
这里发生的事情是您正在使用fromIntegral
将Int
转换为某种类型a
,而您正在使用sqrt
转换a
到a
,并且您正在使用truncate
将a
转换回Int
。根据这些功能的约束,GHC知道a
必须是Floating
和RealFrac
,但是不知道a
是。为了解决这个问题,GHC维护了一组默认规则;在这种情况下,它们声明Floating
或RealFrac
的任何歧义类型默认为Double
。不过,默认情况并非在所有情况下都是预期的行为,因此GHC还会显示警告。
添加类型签名时,歧义将被消除,这就是消息消失的原因。但是添加类型签名有点麻烦。有没有更好的办法?其实有!首先,您需要通过在文件顶部放置以下编译指示来启用TypeApplications
扩展名:
{-# LANGUAGE TypeApplications #-}
此扩展名使您可以将语法@SomeType
用作任何函数的第一个参数;如果函数的签名中包含任何类型变量,则此专门到SomeType
的第一个。 (后续用途专门用于第二,第三,第四等类型变量。)在这种情况下,我们可以选择放置类型应用程序的位置。我们可以将其放在fromIntegral
上:
intSqrt x = truncate $ sqrt $ fromIntegral @_ @Double x
(请注意,fromIntegral
有两个类型变量,因此我们将第一个变量推导出为Int
,而仅将第二个变量专门化。)
或者我们可以将其放在sqrt
上:
intSqrt x = truncate $ sqrt @Double $ fromIntegral x
或在truncate
上
intSqrt x = truncate @Double $ sqrt $ fromIntegral x
这些变体中的任何一个都可以简洁地解决问题。