[英]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))
當我查詢toRadians
和lawOfCosines
的類型時:
*Main> :t toRadians
toRadians :: (Floating a, Integral a) => a -> a
*Main> :t lawOfCosines
lawOfCosines :: (Floating a, Integral a) => a -> a -> a -> a
*Main>
有人可以向我解釋這里發生了什么嗎? 為什么我寫的“直觀”類型簽名實際上是不正確的?
問題出在toRadians
: mod
的類型為Integral a => a -> a -> a
,因此, deg
的類型為Integral i => i
(因此Int
或Integer
)。
然后你嘗試使用/
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 a
和Integral 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 Integral
將Integral
數轉換為Integer
,而不會損失精度。 在某種程度上, Integral
捕獲了數學整數的存在(的一個子集)的本質。
另一方面, Floating
包含諸如pi
和exp
,它們具有明顯的“實數”風格。
如果有一個同時屬於Floating
和Integral
的類型,你可以編寫toInteger pi
並且有一個等於3.14159的整數...... - 這是不可能的:-)
也就是說,您應該更改所有類型的簽名以使用Double
而不是Int
; 畢竟,並非所有三角形都有整數邊,或者是整數度數的角度!
如果你因為某種原因絕對不希望這樣,你還需要將lawOfCosines
中的lawOfCosines
( a
和b
參數) lawOfCosines
為Double
。 這是可能的
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
或類似的操作。 (例如, /
不適用於Int
, pi
不適用於Int
, sqrt
不適用於Int
......)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.