The previous post compared a static and dynamic language parsing an integer from a string. Functions took input from the user, rather than as function parameters. What if functions have parameters?

Some argue that typing function signatures and parameters leads to effortless refactoring, but is the type system the major factor, or is it adhering to simple, short, meaningful functions instead?

Statically Typed Function Parameters

fn read_items(i: i64, s: String) {
    println!("{} / {}", i, s);
}

fn main() {
    read_items(5, "ok");
}

Despite providing a string, and expecting a String, the compiler refuses as there is also a str type in play:

expected struct `std::string::String`, found `str`

There are two types of strings, and they are incompatible. Not in the typical unicode, bytes, or linguistic way, but in the most basic way that quoting simple characters is determined to be incorrect.

fn read_items(i: i64, s: String) {
    println!("{} / {}", i, s);
}

fn main() {
    read_items(5, "ok".to_string());
}

Explicitly converting the str using to_string() works and “5 / ok” is printed, after wrestling with the type system. No apparent bugs were avoided in this simple exercise, with “ok” being fully known at compile time.

Dynamically Typed Function Parameters

(defn read-items [i s] (println i " / " s))

(defn -main []
    (read-items 5 "ok"))

Clojure is successfully able to determine “ok” is a string, and print it, without any additional effort.

Comparison

It isn’t clear that static typing has avoided bugs or defects, outside of type system created scenarios (str vs String), or that it enables programmers to write code which is certainly correct.

Rust provides the programmer with feedback that the type signatures do not match, which is a good catch when a programmer is spending the time to specify them. If the function parameters were both of the same type (common) such as BigDecimal, Rust has no ability to aid the programmer:

fn tax_refund(ssn: uint32, gross_income: BigDecimal, net_income: BigDecimal)

Passing in net_income in place of gross_income is not detected. The programmer needs to understand the function’s code and double check their usage (ideally validated with tests) despite the type information. Rust allows for additional, more descriptive types, however dynamic languages also provide constructs for this category of validation.

There is no indication that using a static language improves refactoring safety such as annotating i64 type signatures, as this can be passed as a input in the wrong position to functions, misnamed, incorrect logic or errors obtaining the contents during data parsing.

In both scenarios the best practice remains careful implementation, peer review and automated testing.