簡體   English   中英

Haskell中的非直觀類型簽名

[英]Unintuitive type signature in Haskell

我做了這個(我認為是)相當簡單的代碼來計算三角形的第三面:

toRadians :: Int -> Double
toRadians d = let deg = mod d 360
              in deg/180 * pi

lawOfCosines :: Int -> Int -> Int -> Double
lawOfCosines a b gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma))

但是,當我嘗試將其加載到GHCi時,我收到以下錯誤:

[1 of 1] Compiling Main             ( law_of_cosines.hs, interpreted )

law_of_cosines.hs:3:18:
    Couldn't match expected type `Double' with actual type `Int'
    In the first argument of `(/)', namely `deg'
    In the first argument of `(*)', namely `deg / 180'
    In the expression: deg / 180 * pi

law_of_cosines.hs:6:26:
    No instance for (Floating Int)
      arising from a use of `sqrt'
    Possible fix: add an instance declaration for (Floating Int)
    In the expression: sqrt
    In the expression:
      sqrt $ a * a + b * b - 2 * a * b * (cos (toRadians gamma))
    In an equation for `lawOfCosines':
        lawOfCosines a b gamma
          = sqrt $ a * a + b * b - 2 * a * b * (cos (toRadians gamma))

law_of_cosines.hs:6:57:
    Couldn't match expected type `Int' with actual type `Double'
    In the return type of a call of `toRadians'
    In the first argument of `cos', namely `(toRadians gamma)'
    In the second argument of `(*)', namely `(cos (toRadians gamma))'

事實證明,修復是刪除我的類型簽名,它工作得很好。

toRadians d = let deg = mod d 360
              in deg/180 * pi

lawOfCosines a b gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma))

當我查詢toRadianslawOfCosines的類型時:

*Main> :t toRadians
toRadians :: (Floating a, Integral a) => a -> a
*Main> :t lawOfCosines
lawOfCosines :: (Floating a, Integral a) => a -> a -> a -> a
*Main>

有人可以向我解釋這里發生了什么嗎? 為什么我寫的“直觀”類型簽名實際上是不正確的?

問題出在toRadiansmod的類型為Integral a => a -> a -> a ,因此, deg的類型為Integral i => i (因此IntInteger )。

然后你嘗試使用/ on deg ,但是/不帶整數(用div除以積分):

(/) :: Fractional a => a -> a -> a

解決方案是簡單地使用fromIntegral :: (Integral a, Num b) => a -> b

toRadians :: Int -> Double
toRadians d = let deg = mod d 360
              in (fromIntegral deg)/180 * pi

在一個類型簽名中看到Floating aIntegral a總是引發我的內部警報,因為這些類應該是互斥的 - 至少,沒有標准數字類型是兩個類的實例。 GHCi告訴我(以及很多其他的東西):

> :info Integral
...
instance Integral Integer -- Defined in `GHC.Real'
instance Integral Int -- Defined in `GHC.Real'
> :info Floating
...
instance Floating Float -- Defined in `GHC.Float'
instance Floating Double -- Defined in `GHC.Float'

要了解為什么這些類是互斥的,讓我們來看看這兩個類中的一些方法(這將有點手工制作)。 fromInteger in IntegralIntegral數轉換為Integer ,而不會損失精度。 在某種程度上, Integral捕獲了數學整數的存在(的一個子集)的本質。

另一方面, Floating包含諸如piexp ,它們具有明顯的“實數”風格。

如果有一個同時屬於FloatingIntegral的類型,你可以編寫toInteger pi並且有一個等於3.14159的整數...... - 這是不可能的:-)


也就是說,您應該更改所有類型的簽名以使用Double而不是Int ; 畢竟,並非所有三角形都有整數邊,或者是整數度數的角度!

如果你因為某種原因絕對不希望這樣,你還需要將lawOfCosines中的lawOfCosinesab參數) lawOfCosinesDouble 這是可能的

lawOfCosines aInt bInt gamma = sqrt $ a*a + b*b - 2*a*b*(cos (toRadians gamma)) where
    a = fromInteger aInt
    b = fromInteger bInt

toRadians的類型簽名表示它需要一個Int但返回一個Double 在某些編程語言中,從一個到另一個(但不是后退)的轉換會自動發生。 哈斯克爾不是這樣的語言; 您必須使用fromIntegral 手動請求轉換。

您看到的錯誤都來自於不適用於Int各種操作,或者嘗試將Int添加到Double或類似的操作。 (例如, /不適用於Intpi不適用於Intsqrt不適用於Int ......)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM