lim*_*imp 3 haskell type-signature
我做了这个(我认为是)相当简单的代码来计算三角形的第三面:
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))
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试将其加载到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))'
Run Code Online (Sandbox Code Playgroud)
事实证明,修复是删除我的类型签名,它工作得很好.
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))
Run Code Online (Sandbox Code Playgroud)
当我查询的类型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>
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释这里发生了什么吗?为什么我写的"直观"类型签名实际上是不正确的?
ami*_*dfv 11
问题是在toRadians:mod具有类型Integral a => a -> a -> a,因此,deg具有式Integral i => i(所以无论是Int或Integer).
然后尝试使用/on deg,但/不使用整数(除以积分div):
(/) :: Fractional a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)
解决方案是简单地使用fromIntegral :: (Integral a, Num b) => a -> b:
toRadians :: Int -> Double
toRadians d = let deg = mod d 360
in (fromIntegral deg)/180 * pi
Run Code Online (Sandbox Code Playgroud)
一起看到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'
Run Code Online (Sandbox Code Playgroud)
要了解为什么这些类是互斥的,让我们来看看这两个类中的一些方法(这将有点手工制作).fromInteger在Integral一个变换Integral号码到Integer,没有精度损失.在某种程度上,Integral捕获了数学整数的存在(的一个子集)的本质.
另一方面,Floating包含诸如pi和的方法exp,其具有明显的"实数"风味.
如果有一个类型都是Floating和Integral,你可以写,toInteger pi并有一个等于3.14159的整数...... - 这是不可能的:-)
也就是说,您应该更改所有类型的签名Double而不是使用Int; 毕竟,并非所有三角形都有整数边,或者是整数度数的角度!
如果你因为某种原因绝对不想要它,你还需要将side(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
Run Code Online (Sandbox Code Playgroud)