
I'd argue that this is good practice for beginners especially, but as you get to be more practiced, your mileage may vary. Certainly, if you're only writing first-order code, this is perhaps less useful. But the moment you begin to pass around higher-order functions with abandon, it becomes quite useful. Say, for example, that I want to write a function to abstract a data- handling pattern I've noticed. I have a number of records I want to query. Each time, I first restrict them to those where a certain field is present. Then, I sort them by some arbitrary ordering. Finally, I return a list of records grouped by some key, as processed by some combining function. Now there's a bunch of steps there to think through, and I might not even have a good name for the function yet to express what it does, but I can write the type straight off the bat: foo :: (Ord c) => (a -> Maybe b) -> (a -> a -> Ordering) -> (a -> c) -
([a] -> d) -> [d]
This at least begins to articulate the intent of this (admittedly, made up, but not actually all that made-up) function, and for me at least, helps me to think about what to write next. Another way to think about this is sometimes you know the type of the function you want simply from context: say that you have some list of data, and you know you want to express a foldr over it. You may not know where to begin with the function that you want to foldr, but you do know, at least, that it needs to be of type a -> a -> b, and if you figure out what the a is and what the b is, you're much closer to being able to write the function itself. Another advantage of this is that, once you've specified a correct type signature, then rather than having a function that produces something totally off the wall, but which you only realize elsewhere in the program, you'll get a nice helpful type error right at the point where you did something weird. Cheers, S. On Mar 23, 2009, at 10:02 PM, Zachary Turner wrote:
Everything I've read has said that it's generally considered good practice to specify the full type of a function before the definition. Why is this? It almost seems to go against the principles of type inference. Why let the compiler infer types if you're just going to tell it what types to use for everything? Ok well, not really for everything, you don't typically specify types for local bindings, but still why the inconsistency? I have a little experience with ML and F# and there you're encouraged to - not- specify types explicitly, and let the compiler infer them for you. In fact, it seems like it would be fairly common where you specify a type that is -less- general than the type that the compiler would infer for you, because the most general type might involve a combination of type classes used in various ways, and it may not always be obvious to the programmer how to specify it correctly. _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners