Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
AlexG
Jul 15, 2004
If you can't solve a problem with gaffer tape, it's probably insoluble anyway.

Smoke_Max posted:

Hi, Haskell newbie here. I was wondering why main doesn't have a [String] -> IO () type signature like other programming languages, like C/C++ or Java. It would be pretty cool, thanks to pattern matching, to be able to do things like this:

code:
main [arg1, arg2] = ...

main _ = do
	name <- getProgName
	putStrLn ("usage: " ++ name ++ " arg1 arg2")

This idea is pretty close to what main's signature actually is, if you imagine "IO a" to mean "World -> (a, World)", where World is the type of the rest of the universe. The main function takes a World, emits a value, and also yields a new World, just like other IO actions. Composing IO actions together amounts to threading successive World states through the calls. Unspecified system magic takes care of providing the initial World to the main function at startup. Part of the World is the arguments provided to the program. These are accessible not just in main, but anywhere that we have access to a World. Alternatively, it could make sense to split them off, so main had effective type [String] -> World -> ((), World); and then the arguments could be passed around explicitly wherever they were needed. Or the arguments could be provided as some global constant "args :: [String]", without even an IO type. (For example, this happens with System.Info.os :: String, since we expect the operating system name to be constant over the running life of the program). It so happens that Haskell decided to treat program arguments as part of the World.

Now, we don't actually have access to World values for a few related reasons. One is that the design of IO as an opaque type allows implementers to not have to provide Worlds as a collection of state - IO can be implemented in other ways, much more conveniently. Also, if we could inspect World values, then we could do things which IO is designed to forbid, like saving World copies for later. So all the "get state from the system" style of actions ultimately need to make use of some builtin special functions to do the work, and have an IO type in order to achieve the proper sequencing of actions. The getArgs function is like this; with type "IO [String]" it is as if there is a World coming in (to pull some Strings from), which is provided by some call chain, within the IO monad, up to the initial state of the World provided to main.

Another trick, by the way, is:
code:
import System.Environment

main :: IO ()
main = ...

-- System.Environment.withArgs :: [String] -> IO a -> IO a
-- flip withArgs :: IO a -> [String] -> IO a
-- flip withArgs main :: [String] -> IO ()
mainWithArgs = flip withArgs main

-- now I can call mainWithArgs ["foo", "bar"]
-- and inside main, getArgs will yield ["foo", "bar"]

Adbot
ADBOT LOVES YOU

  • Locked thread