fromIntegral
会返回Num
数据类型。但似乎Num
能够强制执行Double
或Integer
而没有任何问题。类似地,read
函数能够返回适合其类型签名所需的任何内容。这是怎么回事?如果我确实需要制作类似的功能,我该怎么做?
答案 0 :(得分:6)
类型检查器不仅可以推断函数参数的类型,还可以推断返回类型。实际上那里没有特殊情况。如果您将fromIntegral
或read
的结果存储在Integer
中,则会调用此类型的版本。您可以用同样的方式创建自己的功能。
例如:
class Foo a where
foo :: a
instance Foo Integer where
foo = 7
instance Foo String where
foo = "Hello"
x :: Integer
x = 3
main = do
putStrLn foo
print (foo + x)
由于putStrLn
的类型为String -> IO ()
,因此类型检查器会发现foo
中putStrLn foo
的类型必须为String
才能编译程序。 foo
的类型为Foo a => a
。因此,它推导出a == String
并搜索Foo String
实例。存在这样的实例,它会导致在那里选择foo :: Foo String => String
值。
类似的推理发生在print (foo + x)
。 x
已知Integer
,因为(+)
的类型为Num a => a -> a -> a
,类型检查器会推断加法运算符的左参数也必须为Integer
所以它会搜索Foo Integer
个实例并替换Integer
变体。
在C ++中没有从函数参数到(可能)返回类型的指示。类型检查器甚至可以基于对函数预期返回的知识来推断函数参数。另一个例子:
twice :: a -> [a]
twice a = [a,a]
y :: [Integer]
y = twice foo
此处的函数参数类型为Foo a => a
,这不足以决定使用哪个foo
。但由于结果已知为[Integer]
,因此类型检查器发现它必须向Integer
提供twice
类型的值,并通过使用{{1}的相应实例来实现}。
答案 1 :(得分:2)
read
是typeclass的成员,这意味着它的实现可能取决于类型参数。
此外,1
,42
等数字文字是函数调用fromInteger 1
,fromInteger 42
等的合成糖,以及fromInteger
本身是Num类型类的成员。
因此,文字42
(或fromInteger 42
)可以返回Int
或Double
或任何其他Num实例,具体取决于调用它的上下文。< / p>
答案 2 :(得分:1)
我在这里对术语有点特别,因为我认为你使用的词背叛了一些关于Haskell如何运作的误解。
fromIntegral返回Num数据类型。
更确切地说,fromIntegral
将任何具有class Integral
实例的类型作为输入,并且可以返回作为class Num
实例的任何类型。前奏定义如下:
fromIntegral :: (Integral a, Num b) => a -> b
fromIntegral = fromInteger . toInteger
具有Integral
实例的类型实现toInteger
函数,所有具有Num
实例的类型实现fromInteger
。 fromIntegral
使用与toInteger
实例关联的Integral a
将类型a
的值转换为整数。然后,它使用fromInteger
实例中的Num b
将该Integer转换为类型b
的值。
但似乎Num能够强制使用Double或Integer而没有任何问题。
每次使用haskell功能时,它都会被扩展&#34;。它的定义代替函数调用,并且使用的参数被替换为定义。每次在不同的上下文中展开时,它的类型变量都可以有不同的类型。因此,每次扩展函数时,它都会采用某些具体类型并返回某种具体类型。它不会返回在以后某个时间被强迫的无类型的东西。
类似地,read函数能够返回适合其类型签名所需的任何内容。
read :: Read a => String -> a
read
以String
作为输入,可以在任何返回class Read
实例存在的类型值的上下文中使用。当在程序执行期间扩展它时,这种类型是已知的。它使用Read a
实例中的特定定义将字符串解析为正确的类型。