1. Static type compiler verification saves tonnes of problems. Try managing a 2MLOC dynamically typed solution and you'll get what I mean.
2. Static typed languages are way easier to refactor as more metadata is available to the tooling.
3. Static typed code is easier to test. The type contract is available over the boundary between the implementation and the test cases.
4. Defined interfaces without leaky abstractions are easier to produce when you have static types.
5. Static types allow the compiler to infer more information about how to compiler your code resulting in faster, more efficient code and lower memory usage plus you don't have to compile an instance of a function with every possible type consideration at runtime.
6. Statically typed data serialises more reliably i.e. goes over the wire easier without behemoth contracts and parsers at each end.
7. Statically typed languages tend to have better numeric accuracy as differences between decimal, floats and integers are always deterministic and there are defined casts and conversions between each during operations.
I could go on...
C is hardly a good representative for an statically typed language. Read Luca Cardelli's "Typeful Programming"[1] to get an overview of what typed programming is all about, and see for yourself how brain-dead C and C++ are in comparison. After that go to Benjamin Pierce's canon, "Types and Programming Languages".
It introduced stronger array types, eliminated Cs automatic void* -> T* conversion and, most importantly, put higher-order functions and types on the table.
It might be a warty syntactical abomination, but it can still hold its own against some of the languages of type extremism.
In general, C's type system is so weak as to be both useless and a hindrance.
6 you're adding metadata with type definitions. Enough metadata is present in entirely statically typed languages to not do this.
7. How do you know if 1.000000000000000000001 is decimal or float?
It's weak in some places but strong in others. Knowing when to use each case effectively is the art.
7. It's a float, unless you made it a Decimal explicitly. Arguably that's the wrong default, but almost no languages default to arbitrary precision/size everything.
No, C's type system is totally unsound. It fails to catch very bad errors (pointer-related issues, null-related issues, etc.) and is perfectly happy to implicitly cast left and right. It's little more than a hindrance.
C has some static type safety, and virtually no run-time type safety. Java on the other hand, has a lot of static type safety, is completely memory safe (barring bugs in the implementation), and checks all coercions dynamically. Python has what Java has dynamically, and nothing statically, but it is still 10 times better than what C has!
Java don't forget suffers as do other languages from lots of nasty things related to types including invalid casts, null reference exceptions etc. When these go phut in production, you're usually in the same situation.
Options, like all collections, can be chained together as such:
for {
x <- a(1)
y <- b(x)
z <- c(y)
} yield z
this expression is of type Option[Int], and will never throw a null pointer exception. (Futures can be chained together with identical syntax)But match exceptions do.
Anyways, if I had to choose between unsound static type checking and sound dynamic type checking, I would definitely choose the latter.
I'd call that non-sense.
You have a typed pointer to a function but that's not a void pointer and the "New C Standard" 6.3.2.3 states that you can't cast a function pointer to a void*.
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
Here the void * is an argument that is passed in the callback as well so that you can update your own struct with the result, for example. Singe the library cannot know anything about your types, void * is the only option.