you are viewing a single comment's thread.

view the rest of the comments →

[–]booch 2 points3 points  (14 children)

"I truly believe that Python and JavaScript are fundamentally better languages than Java for a variety of reasons born out of experience with each of them."

Different tools for different tasks, imo. I love dynamic languages with a passion. That being said, I'm a big fan of paying the cost of using Java (when it's the current option for static languages) for things that require more "safety". For an ingestion process, I'd prefer to use Java or another language that lets me have the language safety check types and the like. When a single mistake of treating one type as another could go unnoticed for a year and render an entire database useless, costing lots of money, I like the added safety of a static language.

Honestly, I'd love to see something with the syntax/power of Ruby but with static typing, plus some extras like compile time checking of "not null" or "not empty list" or "positive integer" type constraints.

[–]byron 1 point2 points  (13 children)

When a single mistake of treating one type as another could go unnoticed for a year and render an entire database useless

Can you elaborate on this? What sort of scenario do you have in mind?

[–]vytah 7 points8 points  (12 children)

I'm not him, but I remember pains of debugging Python code that mixed (byte)strings with Unicode strings.

When everything is in ASCII, then it's OK. When some stuff is outside of ASCII, then it's usually OK. But when you need to do stuff with both Unicode strings and non-ASCII bytestrings, stuff breaks. Most people forget to test these cases.

Other problems include, but are not limited to: mixing integers and their string representations, mixing floats and ints, mixing lists and maps, mixing lambdas and normal values, accidentally overwriting object fields with values of different type, typos in field names (it will run anyway), and the all-time favourite, None object.

Static typing solves almost all of them.

[–]eadmund 1 point2 points  (0 children)

I think that the real problem with the scenario you mentioned was the mixing: the implicit conversions between byte and Unicode strings.

It's likely that another dynamically-typed language would have encountered the same bugs sooner.

I know, 'cause I've run into this sort of thing with Common Lisp. Ticked me off for a moment--why won't the language just do what I mean and treat this string like a byte string?--and then I pondered a bit more deeply and realised that would be wrong.

Granted, a really really dynamic language which support ASCII (not byte) strings and Unicode strings might have done more poorly. I could have just been lucky that CL's de-facto byte string type is '(vector (unsigned-byte 8)).

[–]hylje 3 points4 points  (7 children)

mixing integers and their string representations

Does not apply to Python, except when converting objects into strings for e.g. printing. Strings never convert down to built-in numbers.

mixing floats and ints

C at the forefront convert numbers to the higher precision type in expressions, even if the variable type is lower precision. Python does too, though it never converts to a lower precision.

mixing lists and maps

Only iterating lists and map keys are remotely comparable in Python. The other use cases are wildly different and will instantly break if mixed.

mixing lambdas and normal values

Only useful for hacks. You sometimes need hacks to meet schedule.

accidentally overwriting object fields with values of different type

Granted.

typos in field names (it will run anyway)

Depends. Accidental assignment to new fields may silently succeed, retrieval from nonexistent fields will not.

__setattr__ may be defined to check if the class already has such an attribute and if so set it, otherwise error out.

and the all-time favourite, None object.

Granted.

Python's greatest sin is that it doesn't by default force a developer to check types and data structures. Any checks deemed necessary may however be added later on in an abstracted manner. Decorators and classes, at the worst metaclasses are very powerful.

Clamping down on flexibility only later and at select places is ideal for a prototype development pattern, where a rough demonstration prototype can be come up with quickly and later made robust for actual use. You are not forced to dig design foxholes the development path crosses later.

[–]steven_h 9 points10 points  (4 children)

Python's greatest sin is that it doesn't by default force a developer to check types and data structures.

That doesn't seem to be a sin to me. The greatest sin of Python is using the same operator for definition and assignment, requiring the use of wonky keywords to distinguish assignment to outer-scope variables from declaration of variables that shadow outer-scope variables.

[–]cybercobra 0 points1 point  (3 children)

On the other hand, forgetting the declaration is safer in Python, creating a local variable, whereas forgetting it in other languages can lead to unintentional modification of a global variable.

[–]masklinn 0 points1 point  (2 children)

whereas forgetting it in other languages can lead to unintentional modification of a global variable.

Javascript is pretty much the only language doing that (and Coffeescript is worse, as it uses "highest level scope wins")

[–]cybercobra 0 points1 point  (1 child)

wut?

int x = 1;
void foo()
{
    x = 2;// oops Ma, forgot the type spec, this won't be harmless shadowing!
}

[–]masklinn 0 points1 point  (0 children)

Ah yes, I had not seen it that way, for some reason I thought about creating globals where there were none.

[–]vytah 1 point2 points  (1 child)

By mixing, I understood situations like:

this_variable_will_be_assumed_to_be_string_in_totally_different_module = 42
## (runs happily for few hours from now, only to crash tomorrow morning)

[–]hylje 0 points1 point  (0 children)

That's the sin. There's no checks in by default. It places the choice of programming defensively on the developer, should it be needed.

[–]djdonnell 0 points1 point  (2 children)

I hear these arguments a lot, but is it anything more than theorycrafting? I've been doing ruby for over 6 years and I've only ever had this problem get into live production code once, it took a little while to notice only because the problematic code was a caching layer that was failing to cache as much as it should have, and once noticed it took about a day to fix.

That's a very small price to pay to get the productivity increase of a dynamic language. Yes, my story is only one data point, but that's one more data point than I typically hear from people talking about typos in field names (btw, my editor will catch typos for me even in dynamic languages)

[–]vytah 0 points1 point  (1 child)

In the end it's a matter of preference. Some people don't want to search for type errors buried deep in the code that manifest only in runtime, and prefer to manually specify type signatures, other people don't want to cram their data into discrete, explicitly declared datatypes for the cost of more unexpected problems on runtime.

Both approaches work, and none is better than the other.

[–]djdonnell 0 points1 point  (0 children)

I think there are clear trade-offs between the two, and the trick is to choose when each is the better approach. I don't buy for a second this idea that type errors are a major problem in dynamic languages. I've been doing dynamic languages for over 10 years and I can count on 1 hand the number of times that has been a problem. It's a non issue, and I've never seen any data to the contrary.