Monads are red herrings, if what you're interested in is getting started with functional programming (or even Haskell specifically). It's an unfortunate flaw in the Haskell literature that makes it seem like you have to grok monads to learn Haskell; you don't.

Monads enable syntactic sugar. If you're getting started with Haskell, you'll notice that when you write code that does I/O, you use a weird imperative-looking syntax ("do notation") that looks different to the normal functional style:

main :: IO ()
main = do
putStrLn "Please enter your name"
name <- getLine
putStrLn ("Hello " ++ name ++ "!")

'putStrLn "Hello"' doesn't actually print anything; it returns an IO value that, when interpreted by the Haskell runtime, prints something: that interpretation takes place when 'main' is run. The do notation is syntactic sugar for creating a sort of compound IO value that interprets as a bunch of actions rather than just one. The reason you can use do notation for IO but not for (all) other Haskell code is that IO values are monadic (which is another and possibly more useful way of saying "IO is a monad"): that just means that they behave in a certain way when combined (to produce that compound value) and obey certain laws to make the do notation well behaved.

Monads are a design pattern to abstract away repetitive functional code. For example, if you write a lot of pure-functional code you sometimes end up having to pass around a 'state' parameter. If a function reads or writes from the state, then any function that calls it has to pass in the state, and so on. This leads to a lot of functions with an extra state parameter, many of which don't even care about the state except to pass it down the call tree. An improvement would be to write a higher-order function which took a function which didn't care about state and wrapped it to produce one that just passed the state through. The State monad encapsulates this wrapping and lets you clean up the rest of your code.

Monads are what's between the lines of an imperative program. Bourne shell scripts have the default error-handling rule that they ignore failing commands unless it's the last one executed, or put another way, they run in a monad that ignores errors:

cat nonexistent.txt # prints an error message
echo Done. # still executes, prints "Done."
# script returns success

But you can tell it to run instead in a monad which stops after the first error:

set -e # change the monad, i.e. the error-handling rule
cat nonexistent.txt # prints an error message
echo Done. # never runs
# script returns failure

Old-school Visual Basic has the opposite default, but you can make it behave like a shell script with "ON ERROR RESUME NEXT". Yes, VB had monads.

Monads are a specific example of higher-kinded polymorphism, which is a very powerful and useful concept that's starting to enter the programming mainstream (examples are template concepts in C++ and LINQ in C# and .NET). Parametric polymorphism (List<String> in Java and C#, etc) means you don't have to write a 'reverse' function that works on lists of strings, and another 'reverse' function for lists of ints, etc; you can just write a generic reverse function that works on lists of any kind, because it doesn't need to know the details of the contained type, just how to iterate over a list. However, if you want to reverse an array, you still have to write a new 'reverse'. If your language supports higher-kinded polymorphism, you can write a generic reverse function that works on any kind of container, because all it needs to know is that the container has some well-behaved method of iteration.

(The LINQ example is a bit more subtle, because C# doesn't support higher-kinded polymorphism, but LINQ is a hard-coded exception to that rule. The cool LINQ syntax works for any type which provides certain operations and behaves in a certain way - which just happen to coincide with the requirements for the type to be monadic.)