1) definitions are hard, and don't mean much
2) there are many equivalences, and some idioms are conceptually very similar across different type systems, and are just not implemented the same way. For example, a it's a common hobby among FP weenies to show how most of the GoF design patterns are one-liners in their languages of choice.
3) the scope of many typing disciplines is not the same; for example, unityped languages work great for tasks of limited scope with lots of user interface components, functional is historically used to write nice algorithms, and OOP is an attempt at giving a distinct shape to software of very large scope (and it happens to be a pain in the ass when the scope isn't that large)
def compare(a: Int, b: Int): Int
This one has not only 232 possible results, but an incalculable (232)264 possible implementations. Even if we are generous and only allow the inputs to be 0 or 1, we still have 2128 implementations. If the only meaningful return values are -1, 0 and 1, indicating “less than”, “equal” or “greater than”, then why would we embed them like specks of dust in a big desert of undefined behaviour?
Weak typing usually implies a system where types get coerced easily:
php -r "echo '4' + 4;"
ruby -e "'4' + 4"
-e:1:in `+': no implicit conversion of Fixnum into String (TypeError)
from -e:1:in `<main>'
I deliberately used "weak" in the title as a loose umbrella term for the sundry malpractices detailed below. The article is hopefully otherwise quite specific.
In particular, by "types", I always mean "static types" or "propositions". What you mean mean by "dynamic types", I call "tags"; they are not the topic of the article. These usages are standard, if not universally adhered to; I hope I made this clear in the article.
def findAddress(userId: String): String
What does it do? Are you sure?
Now lets look at another:
def blah(foo: UserId): Address
def find_address_of_user( UserID )
address = our_models.AddressModel( … )
In some scenarios, Hungarian notation might make sense:
billing_vcard = contact_vcard(customer.billing_contact)
display_vcard = contact_email(customer.billing_contact)
OO lore has it that pattern matching is evil, and that subtype-polymorphism is the answer to all questions.
That the author of this piece has found various languages educational in presenting new ways of solving problems (with code smells, data model smells, or sloppy architecture) is the biggest take-home message for me: don't be a "Perl programmer" or a "Scala programmer". Be a software engineer, and make sure you are aware of all the available architecture rules, modelling guides and design patterns that have been developed over the years to make your job easier.
Perhaps I'm missing something due to having read through the article too quickly. Please educate me.
There's no criticism implied of my colleagues at all -- I'm as culpable as anyone. The point is though, we can make big improvements with types, without paying a big cost.
You must have had good OOD teachers:
Do I fight management to get the two weeks I'll need to write feature X correctly, or just take the obvious shortcuts to get it done in 1 week?
Of course my laziness shows through because I haven't been in management's ear for long enough beforehand that they decided to give me one week without consulting with me ;)
I display this "lack of bottom-up management" failure mode consistently, I'm more interested in writing code :\
As for my OOD teachers, I've had mountains of bad Perl and PHP code to wade through, and the benefit of Stack Overflow and the Django Project to guide my thinking on the matter.
Type systems do provide some compiler-level assistance in the march towards coherent, well designed software, but they won't solve problems like
def position_sprite( top_left: point, sprite: sprite)
With phantom types, a the Point "type" would be a function of types to types. It helps when some operation work on all kinds of points: they can be polymorphic with respect to the additional type.
data Point a = Point Int Int
draw_sprite :: Point Drawing -> sprite -> Io ()
draw_button :: Point Window -> sprite -> Io ()
translate_point :: Point a -> Point b -> Point a
translate_point (Point x y) (Point xt yt) =
Point (x + xt) (y + yt)
The point I got out of this fine article is that we are under/misusing types.
Types encode logical propositions. The compiler, besides producing binary code, is also a proof checker. We should let it help us reason about the program instead of trying to do everything informally in our heads.
OOP is a valid way to write strongly typed code, but there are many useful and common situations that can be solved by some primitive feature found in any good functional language, where the OOP solution is verbose and counter-intuitive.
And that partly explains why so many people bypass the properly designed, strongly typed, object-oriented solution and write shitty code instead.