【发布时间】:2019-03-31 07:44:38
【问题描述】:
我正在尝试实现一个简单的功能:totient:
coprime :: Integral a => a -> a -> Bool
coprime a b = gcd a b == 1
totient :: Integral a => a -> a
totient m = length $ filter (coprime m) [1..m-1]
ghci> :load 99problems.hs
[1 of 1] Compiling Main ( 99problems.hs, interpreted )
99problems.hs:250:13: error:
• Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
the type signature for:
totient :: forall a. Integral a => a -> a
at 99problems.hs:249:12
• In the expression: length $ filter (coprime m) [1 .. m - 1]
In an equation for ‘totient’:
totient m = length $ filter (coprime m) [1 .. m - 1]
• Relevant bindings include
m :: a (bound at 99problems.hs:250:9)
totient :: a -> a (bound at 99problems.hs:250:1)
Failed, modules loaded: none.
我尝试在(m-1) 上使用fromIntegral 或toInteger 之类的东西,但都没有奏效。我不确定我在这里缺少什么......似乎Int 应该是Integral a => a 类型。怎么了?
【问题讨论】:
-
length的类型是length :: Foldable t => t a -> Int。但是您明确表示totient应该返回Integral a。 -
正如答案所解释的,
fromIntegral (Num b, Integral a) => A -> b意味着调用者(length函数)需要为其提供任何Integral a。Int是一个Integral所以这检查出来了。然后,fromIntegral返回一个Num b,用户(totient函数)选择它的类型。在这种情况下,您在类型声明中选择它为Integral。 -
注意,当你“选择”返回的类型时,你不需要限制它,在这种情况下你可以返回
Num b,它仍然有效。 -
您也可以使用
genericLength(来自Data.List)而不是length来修复它。length的返回类型是单态的,这可能很烦人,这似乎是标准库的初始设计中的一个错误,我们希望在未来的版本中进行更改。 -
但是
fromIntegral正在返回一个通用的Num类型,然后你选择你想要的类型。您可以按原样离开,也可以像您正在做的那样将其“投射”到Integral