在Haskell中强制数据类型时的简洁代码格式

时间:2019-06-08 22:25:44

标签: haskell

作为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),但是这样做的代价是添加两对括号。

在此功能中是否有办法同时兼顾两个方面:即简洁的代码和警告的抑制?

1 个答案:

答案 0 :(得分:10)

这里发生的事情是您正在使用fromIntegralInt转换为某种类型a,而您正在使用sqrt转换aa,并且您正在使用truncatea转换回Int。根据这些功能的约束,GHC知道a必须是FloatingRealFrac,但是不知道a。为了解决这个问题,GHC维护了一组默认规则;在这种情况下,它们声明FloatingRealFrac的任何歧义类型默认为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

这些变体中的任何一个都可以简洁地解决问题。