简体   繁体   English

两个相似的函数如何在Haskell中具有不同的多态类型?

[英]How can two similar functions have different polymorphic types in Haskell?

Im pretty much new to Haskell, so if Im missing key concept, please point it out. 我几乎是Haskell的新手,所以如果我缺少关键概念,请指出。

Lets say we have these two functions: 让我们说我们有这两个功能:

fact n
     | n == 0 = 1
     | n > 0  = n * (fact (n - 1))

The polymorphic type for fact is (Eq t, Num t) => t -> t Because n is used in the if condition and n must be of valid type to do the == check. fact的多态类型是(Eq t, Num t) => t -> t因为n在if条件中使用,n必须是有效类型才能进行==检查。 Therefor t must be a Number and t can be of any type within class constraint Eq t 因此t必须是Number并且t可以是类约束Eq t的任何类型

fib n
    | n == 1 = 1
    | n == 2 = 1
    | n > 2  = fib (n - 1) + fib (n - 2)

Then why is the polymorphic type of fib is (Eq a, Num a, Num t) => a -> t ? 那么为什么多晶型的fib(Eq a, Num a, Num t) => a -> t

I don't understand, please help. 我不明白,请帮忙。

Haskell always aims to derive the most generic type signature. Haskell总是旨在推导出通用的类型签名。

Now for fact , we know that the type of the output , should be the same as the type of the input: fact ,我们知道输出类型应该与输入的类型相同:

fact n | n == 0 = 1
       | n > 0  = n * (fact (n - 1))

This is due to the last line. 这是由于最后一行。 We use n * (fact (n-1)) . 我们使用n * (fact (n-1)) So we use a multiplication (*) :: a -> a -> a . 所以我们使用乘法(*) :: a -> a -> a Multiplication thus takes two members of the same type and returns a member of that type. 因此乘法采用相同类型的两个成员并返回该类型的成员。 Since we multiply with n , and n is input, the output is of the same type as the input. 由于我们乘以n ,并且输入n ,因此输出与输入的类型相同。 Since we use n == 0 , we know that (==) :: Eq a => a -> a -> Bool so that means that that input type should have Eq a => , and furthermore 0 :: Num a => a . 由于我们使用n == 0 ,我们知道(==) :: Eq a => a -> a -> Bool ,这意味着该输入类型应该具有Eq a => ,而且0 :: Num a => a So the resulting type is fact :: (Num a, Eq a) => a -> a . 所以结果类型是fact :: (Num a, Eq a) => a -> a

Now for fib , we see: 现在对于fib ,我们看到:

fib n | n == 1 = 1
      | n == 2 = 1
      | n > 2  = fib (n - 1) + fib (n - 2)

Now we know that for n , the type constraints are again Eq a, Num a , since we use n == 1 , and (==) :: Eq a => a -> a -> Bool and 1 :: Num a => a . 现在我们知道对于n ,类型约束又是Eq a, Num a ,因为我们使用n == 1(==) :: Eq a => a -> a -> Bool1 :: Num a => a But the input n is never directly used in the output . 输入n从不直接用于输出 Indeed, the last line has fib (n-1) + fib (n-2) , but here we use n-1 and n-2 as input of a new call . 实际上,最后一行有fib (n-1) + fib (n-2) ,但在这里我们使用n-1n-2作为新调用的输入 So that means we can safely asume that the input type and the output type act independently. 这意味着我们可以安全地假设输入类型和输出类型独立地起作用。 The output type, still has a type constraint: Num t : this is since we return 1 for the first two cases, and 1 :: Num t => t , and we also return the addition of two outputs: fib (n-1) + fib (n-2) , so again (+) :: Num t => t -> t -> t . 输出类型仍然有一个类型约束: Num t :这是因为前两个情况返回1 ,而1 :: Num t => t ,我们还返回两个输出: fib (n-1) + fib (n-2) ,所以再次(+) :: Num t => t -> t -> t

The difference is that in fact , you use the argument directly in an arithmetic expression which makes up the final result: 不同之处在于, fact ,您直接在算术表达式中使用参数,该算术表达式构成最终结果:

fact n | ...  = n * ...

IOW, if you write out the expanded arithmetic expression, n appears in it: IOW,如果你写出扩展的算术表达式, n出现在其中:

fact 3  ≡  n * (n-1) * (n-2) * 1

This fixes that the argument must have the same type as the result, because 这修复了参数必须与结果具有相同类型的原因,因为

(*) :: Num n => n -> n -> n

Not so in fib : here the actual result is only composed of literals and of sub -results. fib不是这样:这里实际结果只包含文字和子结果 IOW, the expanded expression looks like IOW,扩展的表达式看起来像

fib 3  ≡  (1 + 1) + 1

No n in here, so no unification between argument and result required. 这里没有n ,因此参数和结果之间不需要统一。

Of course, in both cases you also used n to decide how this arithmetic expression looks, but for that you've just used equality comparisons with literals, whose type is not connected to the final result. 当然,在这两种情况下,你还使用n来决定这个算术表达式的外观,但为此你刚刚使用了与文字相等的比较,其类型没有与最终结果相关联。

Note that you can also give fib a type-preservig signature: (Eq a, Num a, Num t) => a -> t is strictly more general than (Eq t, Num t) => t -> t . 请注意,您也可以给fib一个类型preservig签名: (Eq a, Num a, Num t) => a -> t严格更普遍比(Eq t, Num t) => t -> t Conversely, you can make a fact that doesn't require input- and output to be the same type, by following it with a conversion function: 相反,你可以做一个fact ,不需要输入和输出是同一类型,通过转换函数如下它:

fact' :: (Eq a, Integral a, Num t) => a -> t
fact' = fromIntegral . fact

This doesn't make a lot of sense though, because Integer is pretty much the only type that can reliably be used in fact , but to achieve that in the above version you need to start out with Integer . 这并不让有很大的意义,但因为Integer是相当多的,可以可靠地被使用的唯一一种fact ,但要实现的是,在以上的版本,你需要有开始了Integer Hence if anything, you should do the following: 因此,如果有的话,您应该执行以下操作:

fact'' :: (Eq t, Integral a, Num t) => a -> t
fact'' = fact . fromIntegral

This can then be used also as Int -> Integer , which is somewhat sensible. 这也可以用作Int -> Integer ,这有点合理。

I'd recommend to just keep the signature (Eq t, Num t) => t -> t though, and only add conversion operations where it's actually needed. 建议只保留签名(Eq t, Num t) => t -> t ,并且只添加实际需要的转换操作。 Or really, what I'd recommend is to not use fact at all – this is a very expensive function that's hardly ever really useful in practice; 或者真的,我建议的是根本不使用fact - 这是一个非常昂贵的功能,在实践中几乎没有用过; most applications that naïvely end up with a factorial really just need something like binomial coefficients , and those can be implemented more efficiently without a factorial. 大多数天真地以阶乘结束的应用程序实际上只需要像二项式系数那样的东西,并且这些应用程序可以在没有因子的情况下更有效地实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Haskell中不同数据类型的函数 - Functions of different Data Types in Haskell 具有不同类型参数的Haskell函数 - Haskell functions with parameters different types Haskell中的多态类型 - Polymorphic types in Haskell 在Haskell中创建多态函数 - Creating polymorphic functions in Haskell JavaScript:如何组合两个不同但非常相似的函数? - JavaScript: How to combine two different but pretty similar functions? 为什么 Golang 允许两个具有不同接收者类型的函数具有相同的名称,但如果它们具有不同的参数类型则不允许? - Why does Golang allow two functions to have the same name if they have different receiver types but not if they have different parameter types? 函数的多态类型作为haskell中的参数? - Polymorphic type of functions as parameter in haskell? 为什么具有相同`id`的两个函数具有不同的属性? - Why can two functions with the same `id` have different attributes? 如何在Haskell中将两个功能合并为一个 - How to merge two functions into one in Haskell 我可以使用lambda指针数组来保存具有不同类型的参数/返回值的函数吗? - Can I have an array of lambda pointers in order to hold functions with different types of arguments/return values?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM