5

I often feel that there's something wrong when I code blocks such as:

function foo (arg){ // arg: a SomeClass object
    ...
}

or

function foo (arg){
    if (Object.prototype.toString.apply(arg) !== '[object Array]')
        throw new Error("Parameter must be an array");
    ...
}

. With a statically typed language I wouldn't have to either clarify the expected argument type or check compliance to this expectation. In addition, the possibility of specifying a return type makes the code even more descriptive.

OTOH some cases lend themselves to self-documenting code:

function parse (json){
    ...
}

. In the example above, it's reasonable enough to suppose that your API consumers will know both what the function is supposed to do, and what argument values will be acceptable.

However other times clarifying the code intent by choosing "clarifying" names would yield long, awkward names that mix the expected type of a variable and its role.

Am I using dynamic languages with the wrong mindset?

What conventions, techniques, patterns can save one from having to write ugly type-checking code and/or excessively depending on comments?

2
  • 1
    I don't quite understand what you are asking. Presumably Someclass is already documented, so when it is passed into function foo, its purpose is already apparent. And I assume you've already chosen a meaningful name for foo. If you're trying to force static typing onto a dynamic language I think you're missing the point. Commented Oct 13, 2011 at 15:09
  • @RobertHarvey you could indeed infer the argument type if you observed a practical example of the function usage. But shouldn't block meaning stand for itself? Especially when building a framework - as opposed to an application.
    – deprecated
    Commented Oct 13, 2011 at 15:24

5 Answers 5

2

Here's the thing.

Statically-typed languages normally have a compiler, and the compiler does type checking to prevent a certain class of bugs at compile time - that is, if you have one of those bugs in your code, you won't even get a chance to run into it in production, because you won't have anything to execute until you fix the bug. This is probably the biggest benefit of static typing.

Dynamic languages (often) do not have a compiler, and consequently, any type checking you might want to do happens at run-time, and if your types don't work out the way you expected, you can only find out through testing. But guess what, if you don't type-check at all, you can also find bugs through testing. The added value of type checking, thus, is zero.

The usual way to go about these things is captured in the Python mantra "easier to ask forgiveness than permission" - instead of checking the types of your arguments, you assume that they are correct, and when this causes problems, you catch the exception and recover.

The idea of duck typing is closely related: I don't care about your type, as long as you implement the methods I need.

There are two downsides to the dynamic approach that you need to be aware of: side effects (when asking forgiveness, you also need to clean up all the side effects you've caused so far), and accidental naming clashes (data.save("example.txt") is conceptually different from rescueBoat.save(swimmer))

14
  • That too seems the thing to me :) cheers for the answer.
    – deprecated
    Commented Oct 14, 2011 at 6:25
  • 1
    I disagree. Writing tests with sufficient coverage is way harder than performing simple type-checks. Also it is better to fail early and with meaningful messages, rather than having arguments of the wrong type being passed around a dozen of times, the failure resulting somewhere completely else than at the place of contract breach, making understanding the error very hard.
    – back2dos
    Commented Oct 14, 2011 at 7:57
  • 1
    @gnat: For one, early failure is always cheaper. Secondly, if I call against any sort of API, I expect it to make sanity checks early on, so that I don't have 3 pages of stack trace I will have to follow deep into no-mans land. Also one core aspect of tests is to have specs in runnable form. In that regard, they are no different from type annotations/checks.
    – back2dos
    Commented Oct 14, 2011 at 8:40
  • 1
    Definitely -1 for finding bugs through testing. Safety 101: Prove safety if possible. Types are a basic and excellent way to do this. You can never prove the absence of bugs with tests in the same way you can with types.
    – DeadMG
    Commented Oct 14, 2011 at 9:46
  • 1
    @gnat: I hardly trust "verified everything" statements. Ultimately this is only achievable through formal analysis. Static analysis is a form of formal analysis and every aspect of your solution that you succeed in embedding into your language's semantics is something the compiler will actually verify for you. From my personal experience, the more I am able to do this, the less bugs I produce. I practically only write unit tests for parts of my code that inevitably escape the language semantics.
    – back2dos
    Commented Oct 14, 2011 at 17:03
7

Am I using dynamic languages with the wrong mindset?

I think so, yes. Worry less about what type it is and more about what functionality it provides.

You can usually check this explicitly at runtime. For example, In Ruby, you can call respond_to on any object and find out if it is capable of responding to the methods you intend to call. Or you can just call the method and it will fail if it can't handle it.

This is known as duck-typing, if you want to search for more info.

1
  • Yes and no. resond_to will actually tell you nothing about the arguments expected for that method. Common names such as "save", "print" etc. make this hard. The approach is a bit more workable with Smalltalkish selectors, because for one they communicate how many arguments are expected and they promote increased descriptiveness for more arguments. IMHO one should either just assume its there and call, or ask whether the object is_a desired type. The advantage here is, that you can rely on the fact, that at some point somebody consciously provided an implementation that returns true.
    – back2dos
    Commented Oct 14, 2011 at 8:29
3

If the argument is 'aDuck', and it '.quack();'s like a Duck and it '.walk()'s like a Duck, then saying that it's of type 'Duck'... might be redundant and misleading, as there may also be other classes completely unrelated to Duck that also '.quack();' like a Duck and '.walk()' like a Duck.

Meaningful names and automated regression tests can be quite useful.

If someone calls your method with something very un-Duck-like and it doesn't work the way they expect, then certainly their tests will fail. And they may learn to do something more appropriate to their needs.

So: Write good code. And don't worry about it too much. ;->

6
  • I had to think hard about static typing being misleading but I think I'm beggining to agree - especially for OO contexts (as opposed to primitive type handling).
    – deprecated
    Commented Oct 13, 2011 at 19:52
  • -1. A duck is a duck. Something that quacks and walks is something that quacks and walks. If I want a duck, than not everything that walks and quacks is good enough for me. I'd rather sit on a chair than anything that returns 4 upon .getLegCount(). Duck typing (or its compile time counterpart structural subtyping) is an alternative to explicit typing, but not a replacement.
    – back2dos
    Commented Oct 14, 2011 at 8:05
  • 1
    @back2dos: there's the cultural difference. In dynamic-languages land, you have to expect to be treated as a duck if you can walk and quack. The upside of this is that you don't need to go through formal routines to get the duck treatment, but the downside is that you may unintentionally get the duck treatment for looking too much like one.
    – tdammers
    Commented Oct 14, 2011 at 9:28
  • @tdammers: I don't agree. In most dynamic languages objects will respond to an "is a"-call to establish explicit type relation ships. Ruby Strings have a strip-method. Does that make them strippers?
    – back2dos
    Commented Oct 14, 2011 at 9:55
  • @back2dos: speaking from Python experience (which, unlike PHP, has a strong coding style culture), 'is-a' is often frowned upon as an avoidable hack, and the general attitude is that you should check for individual features rather than declared type. In a way, this makes sense, because it allows for such things as calling stream reading methods on otherwise unrelated custom objects, as long as they (informally) implement the required interface.
    – tdammers
    Commented Oct 14, 2011 at 10:17
2

I think your mindset is fundamentally sound, as long as you don’t overlook the advantages that a dynamic language gives you amongst which are the reduction in boilerplate, the ability to handle impedance mismatches such as database access and localised expressiveness.

One small tip is to encapsulate the run-time type checking in the form of an assertion. This makes the role of the checking code obvious and is effective when used in conjunction with unit tests.

However, static type declarations do not just provide the means for a compiler to perform a basic set of mandatory unit tests they provide a lot of information to the coder about objects they are using and methods they are calling. Also, providing the assertions and tests to make up for the absence of a compiler’s type checking takes effort, repeated over and over again. I would rather have a single keyword to indicate a type than many lines of assertions and tests.

Although dynamic languages can be very expressive at local level, say within a function, they are far less effective at showing the relationships between components in a large code base. I would rather examine the details of and relationships between objects as revealed by the code in a set of class declarations than examine a bunch of procedural production code and unit tests which may or may not be complete.

I was recently maintaining some unfamiliar javascript code which involved manipulating a tree which was based on some JSON loaded from a server. I had no idea what was guaranteed or even expected about any given node in the tree. A class declaration would have at least given the original developer somewhere to hang information about the expectations about various nodes.

Whereas I might prefer to write in a dynamic language, I certainly prefer to read and maintain code in a statically typed one.

1

My general experience is that 90% of time the role name implies the type. E.g. you may have User class appear as "creator", "owner", "editor" and simply "currentUser" over and over in given application. If you use those identifiers for the roles consistently and take a little bit of care to avoid naming roles of different types similarly (e.g. don't use "owner" for parent objects if you used it for user owning document), anyone using the interface will learn what type of objects should go where quite fast.

Much of the rest will be obvious from context, especially if you also call actions consistently. Using long names or just accepting the name is not descriptive and adding enough documentation should do for the few cases that remain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.