2
votes

I'm teaching myself Haskell, and I've come across two implementations of a "flip" function that raise questions for me about name declaration.

These two do the same thing:

flip'' :: (a -> b -> c) -> b -> a -> c  
flip'' f y x = f x y  

flip' :: (a -> b -> c) -> (b -> a -> c)  
flip' f = g  
    where g x y = f y x  

The first example is as I would expect. In the second example, I'm confused why we're allowed to write g x y = f y x when we haven't declared either x or y yet. I understand that lazy evaluation means neither is evaluated until they're needed, but I expected the compiler to at least want a declaration.

It compiles even without the type signature... this works fine:

flip' f = g  
    where g x y = f y x 

So are x and y just completely untyped variables? Or is something else going on? Why are we able to do this?

3
x and y are polymorphically typed variables. They're not used in any special way, so their type can be anything.AndrewC
@AndrewC thanks - that's a helpful way to think about it, and a great keyword for my further research as well :)Stephen

3 Answers

3
votes

I'm confused why we're allowed to write g x y = f y x when we haven't declared either x or y yet.

Since g, x and y appear to the left of an equal sign, they are, in fact, declared. The where introduces a scope local to the code it is attached to. The code could be written thus:

flip f = let g x y = f y x in g

In english: Let g be a function with two arguments called x and y ....

3
votes

where works exactly the same as declaring a completely new function. The only difference is that function exists in local scope of function within which where was used. This means that code you provided when called is equivalent to:

flip' f -- Equivalent to flip' (\x y -> f x y) 
-- After we call it we have in our environment(scope) function f

g x y = f y x -- Here you call it and it is perfectly fine, because f is defined

What about types? Compiler tries to deduce types for g based on information it has. The information it has is that type of g must be the same as f except for flipped order of arguments, it also notices that f has only two arguments. So type deduction algorithm in general says that if f :: a -> b -> c then g :: b -> a -> c.

2
votes

There is nothing special with the second examples. Also in the first example, the type signature is optional, so the following line works on its own:

flip'' f y x = f x y

In Haskell, you don't have to specify the types of variables, but the Haskell implementation can infer them for you. You can use :t flip'' in ghci to check that the inferred type of flip'' is what you would expect.

So can you use variables without declarations or without type in Haskell? No. You can only use variables that are bound somewhere, for example, because they occur to the left of an = sign in an equation. And every variable has to have a type, even if it is a polymorphic type variable like a, b, and c in the question. It is just that the programmer doesn't have to declare the type to the Haskell implementation, but the Haskell implementation can figure the type out based on the how the variable is used.

Note that this "figuring the type out" business is still static typing. The Haskell implementation first figures out the type of all variables, and then the program is compiled or run. So if there is a type error, you get an error message before the program even starts running.