A simple example: suppose that every web form input is mapped into an "UnsafeUserInput rawtype" (that is, an algebraic data type parametrized over the raw input type). By simply ensuring that your output code only works on "SanitizedUserInput" types, you enforce that at some point in the code you apply a sanitization function to any unsafe input. Now the compiler is working for you, checking that your overall data flow respects the constraints you've encoded into the type system. In this style of programming, the type system becomes a way of encoding your desired integrity constraints inline with the functions and data types rather than outside the application logic in test suites.
string a = get_input_from_user();
string b = sql_lookup(database, a);
insert_into_output_html(b);
requiring instead UserInputString a = get_input_from_user();
SQLResultString b = sql_lookup(database, ConvertUserInputToSQLQueryInput(a))
insert_into_output_html(ConvertSQLResultToHtml(b));http://blog.moertel.com/articles/2006/10/18/a-type-based-sol...
(That was written over six years ago. Today, there are even better solutions that don't just apply static types to strings but to the language fragments within them.)