And in particular, there are a few things I really like:
Hindley Milner-style type systems make life easier. You don't write out very many type annotations, but you get a type safety guarantee. People seem to underestimate just how strong this guarantee really is. It is equivalent to "Your program will not crash" for all intents and purposes. Similarly, I generally find that a program that passes type checking is about 7 trillion* times more likely to be functionally correct than a Java or C++ program that merely compiles.
(Specific to ML) The "Mostly functional" nature of SML means that you usually write pure functional programs. However, it's easy to drop down into something resembling a more imperative style of programming if you need to. I found this helped my learning curve a lot. I could write functional code as far as my limited experience would allow me, and then I could finish the program using my (considerably more extensive) imperative programming experience. I've found that I had done this less and less as I've become more experienced (to the point where I pretty much don't do it at all), but it was a nice option. Supplementary to this point, the presence of mutable arrays occasionally makes sense in terms of performance, and so these are available to you in the rare cases where it makes sense to use them. This certainly isn't knocking purely functional languages such as Haskell that use Monads to achieve much the same effect, I've just found that having a "mostly-functional" approach has suited my brain, and permitted me to write more efficient programs with less effort. Others would probably say exactly the opposite with regards to Haskell vs ML.
Pattern matching. Oh, how I love thee, especially when combined with recursive data types. I don't like to get sentimental about programming languages, but seriously, sometimes it is pure poetry.
Functors give you most of the nice properties of templates in C++ without the warts (at least with respect to the way I use each).
The performance of compiled OCaml and SML code is very similar to that of C++ by some benchmarks. In practice, I've found this to mean "plenty fast enough", even in scientific and numerical computing applications.
Polymorphism and higher-order functions make code re-use a practical reality, as in, you usually write things in a reusable way purely by accident rather than needing to think about how you will make things reusable.
Sorry, this is turning into a love letter, so I'll stop.
* May or may not be the exact figure. I mean "a lot".
And in particular, there are a few things I really like:
Hindley Milner-style type systems make life easier. You don't write out very many type annotations, but you get a type safety guarantee. People seem to underestimate just how strong this guarantee really is. It is equivalent to "Your program will not crash" for all intents and purposes. Similarly, I generally find that a program that passes type checking is about 7 trillion* times more likely to be functionally correct than a Java or C++ program that merely compiles.
(Specific to ML) The "Mostly functional" nature of SML means that you usually write pure functional programs. However, it's easy to drop down into something resembling a more imperative style of programming if you need to. I found this helped my learning curve a lot. I could write functional code as far as my limited experience would allow me, and then I could finish the program using my (considerably more extensive) imperative programming experience. I've found that I had done this less and less as I've become more experienced (to the point where I pretty much don't do it at all), but it was a nice option. Supplementary to this point, the presence of mutable arrays occasionally makes sense in terms of performance, and so these are available to you in the rare cases where it makes sense to use them. This certainly isn't knocking purely functional languages such as Haskell that use Monads to achieve much the same effect, I've just found that having a "mostly-functional" approach has suited my brain, and permitted me to write more efficient programs with less effort. Others would probably say exactly the opposite with regards to Haskell vs ML.
Pattern matching. Oh, how I love thee, especially when combined with recursive data types. I don't like to get sentimental about programming languages, but seriously, sometimes it is pure poetry.
Functors give you most of the nice properties of templates in C++ without the warts (at least with respect to the way I use each).
The performance of compiled OCaml and SML code is very similar to that of C++ by some benchmarks. In practice, I've found this to mean "plenty fast enough", even in scientific and numerical computing applications.
Polymorphism and higher-order functions make code re-use a practical reality, as in, you usually write things in a reusable way purely by accident rather than needing to think about how you will make things reusable.
Sorry, this is turning into a love letter, so I'll stop.
* May or may not be the exact figure. I mean "a lot".