I just learned about currying, and while I think I understand the concept, I'm not seeing any big advantage in using it.

As a trivial example I use a function that adds two values (written in ML). The version without currying would be 

fun add(x, y) = x + y


and would be called as

add(3, 5)


while the curried version is

fun add x y = x + y 
(* short for val add = fn x => fn y=> x + y *)


and would be called as

add 3 5


It seems to me to be just syntactic sugar that removes one set of parentheses from defining and calling the function. I've seen currying listed as one of the important features of a functional languages, and I'm a bit underwhelmed by it at the moment. The concept of creating a chain of functions that consume each a single parameter, instead of a function that takes a tuple seems rather complicated to use for a simple change of syntax.

Is the slightly simpler syntax the only motivation for currying, or am I missing some other advantages that are not obvious in my very simple example? Is currying just syntactic sugar?

With curried functions you get easier reuse of more abstract functions, since you get to specialize. Let's say that you have an adding function 

add x y = x + y


and that you want to add 2 to every member of a list. In Haskell you would do this:

map (add 2) [1, 2, 3] -- gives [3, 4, 5]
-- actually one could just do: map (2+) [1, 2, 3], but that may be Haskell specific


Here the syntax is lighter than if you had to create a function add2

add2 y = add 2 y
map add2 [1, 2, 3]


or if you had to make an anonymous lambda function:

map (\y -> 2 + y) [1, 2, 3]


It also allows you to abstract away from different implementations. Let's say you had two lookup functions. One from a list of key/value pairs and a key to a value and another from a map from keys to values and a key to a value, like this:

lookup1 :: [(Key, Value)] -> Key -> Value -- or perhaps it should be Maybe Value
lookup2 :: Map Key Value -> Key -> Value


Then you could make a function that accepted a lookup function from Key to Value. You could pass it any of the above lookup function, partially applied with either a list or a map, respectively:

myFunc :: (Key -> Value) -> .....


In conclusion: currying is good, because it lets you specialize/partially apply functions using a lightweight syntax and then pass these partially applied functions around to higher order function such as map or filter. Higher order functions (which take functions as parameters or yield them as results) are the bread and butter of functional programming, and currying and partially applied functions enable higher order functions to be used much more effectively and concisely.
