首页 > 解决方案 > 在 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 给出警告 Defaulting the following constraint to type `Double'。我可以通过将结果从 sqrt 强制为 Double 来抑制警告(请参阅 intSqrt2),但代价是添加两对括号。

有没有办法在这个功能中两全其美:即简洁的代码和抑制警告?

标签: haskell

解决方案


这里发生的情况是,您fromIntegral用于将 an 转换Int为某种类型a,您sqrt用于转换aa,并且您truncate用于转换aInt. 根据对这些函数的约束,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

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


推荐阅读