Why does the main function in Haskell not have any parameters?

I tend to agree with chi's answer, that there's no clearly compelling reason it has to be done either way, so it really comes down to a somewhat subjective judgement call that was made by a small group of people a long time ago. There's no guarantee that there is going to be any particularly satisfying reason behind it.

Here is some reasoning that comes to my mind (which may or may not have been something the original designers thought of at the time, or even would agree with).

What we're really love to be able to do is something like:

main :: Integer -> String -> Set Flag -> IO ()

(for some hypothetical program that takes as command line arguments an integer, a string, and a set of flags)

Being able to write small command line programs as if they were just a function of their command line arguments would be great! But that would need the operating system (or at least the shell) to understand the types used in a Haskell program and know how to parse them (and what to do if parsing fails, or if there aren't enough arguments, or etc), which isn't going to happen.

Perhaps we could write a wrapper to do that. It could take care of parsing the raw string command line arguments into Haskell types and generating error messages (if needed), and then call main for us. But wait, we can do exactly that! We just have to call the wrapper main (and rename what we were previously calling main)!

The point is this: if you want to think of your program as a simple function of external inputs, that makes a lot of sense, but main is not that function. main works much better as a wrapper that takes care of the ugly details of receiving input over an untyped interface and calling the function that "really is" your program.

Forcing you to include a call to getArgs in your set up code makes it more apparent there's more to handling command line arguments than just getting access to them, and possibly nudges you to writing some of that extra handling code rather than just writing main (arg1 : arg2 : _) = do stuffWith arg1 arg2.

Also, it is super trivial to convert the interface we have to the one you want:

import System.Environment

main = real_main =<< getArgs

real_main :: [String] -> IO ()
real_main args = print args

So you can have it whichever way you prefer!


It's a language design choice. Neither approach is strictly better than the other one.

Haskell could have been designed to have a main of either kind.

When one does need the program arguments, it would be more convenient to have them passed as function arguments to main.

When one does not need the program arguments, having them passed to main is slightly cumbersome, since we need to write a longer type, and an additional _ to discard the [String] argument.

Further, getArgs lets one access the program arguments anywhere in the program (inside IO), while having them passed to main, only, can be less convenient since one would then be forced to pass them around in the program, which can be inconvenient.

(Short digression) For what it's worth, I had a similar reaction to yours a long time ago when I discovered that in Java we have void main() instead of int main() as in C. Then I realized that in most programs I always wrote return 0; at the end, so it makes little sense to always require that. In Java that's the implicit default, and when we really need to return something else, we use System.exit(). Even if that is the way it's done in a previous language (C, in this case), new languages can choose a new way to make available the same functionality.