Why is function definition for all types at once not allowed in Haskell?


It's a feature, and actually is very fundamental. It boils down to a property known as parametricity in programming language theory. Roughly, that means that evaluation should never depend on types that are variables at compile time. You cannot look at a value where you do not know its concrete type statically.

Why is that good? It gives much stronger invariants about programs. For example, you know from the type alone that a -> a has to be the identity function (or diverges). Similar "free theorems" apply to many other polymorphic functions. Parametricity also is the basis for more advanced type-based abstraction techniques. For example, the type ST sa in Haskell (the state monad), and the type of the corresponding runST function, rely on s being parametric. That ensures that the running function has no way of messing with the internal representation of the state.

It is also important for efficient implementation. A program does not have to pass around costly type information at run time (type erasure), and the compiler can choose overlapping representations for different types. As an example of the latter, 0 and False and () and [] are all represented by 0 at runtime. This wouldn't be possible if a function like yours was allowed.


Haskell enjoys an implementation strategy known as "type erasure": types have no computational significance, so the code that you emit doesn't need to track them. This is a significant boon for performance.

The price you pay for this performance benefit is that types have no computational significance: a function can't change its behavior based on the type of an argument it is passed. If you were to allow something like

f () = "foo"
f [] = "bar"

then that property would not be true: the behavior of f would, indeed, depend on the type of its first argument.

There are certainly languages that allow this kind of thing, especially in dependently typed languages where types generally can't be erased anyway.


For a function a -> Integer there's only one behaviour which is allowed - return a constant integer. Why? Because you have no idea what type a is. With no constraints specified, it could be absolutely anything at all, and because Haskell is statically typed you need to resolve everything to do with types at compile time. At runtime the type information no longer exists and thus cannot be consulted - all choices of which functions to use have already been made.

The closest Haskell allows to this kind of behaviour is the use of typeclasses - if you made a typeclass called Foo with one function:

class Foo a where
    foo :: a -> Integer

Then you could define instances of it for different types

instance Foo [a] where
    foo [] = 0
    foo (x:xs) = 1 + foo xs

instance Foo Float where
    foo 5.2 = 10
    foo _ = 100

Then as long as you can guarantee some parameter x is a Foo you can call foo on it. You still need that though - you can't then write a function

bar :: a -> Integer
bar x = 1 + foo x

Because the compiler doesn't know that a is an instance of Foo . You have to tell it, or leave out the type signature and let it figure it out for itself.

bar :: Foo a => a -> Integer
bar x = 1 + foo x

Haskell can only operate with as much information as the compiler has about the type of something. This may sound restrictive, but in practice typeclasses and parametric polymorphism are so powerful I never miss dynamic typing. In fact I usually find dynamic typing annoying, because I'm never entirely sure what anything actually is.

链接地址: http://www.djcxy.com/p/43026.html

上一篇: 我如何获得Haskell中范围函数的类型签名?

下一篇: 为什么Haskell不允许所有类型的函数定义?