Hacker News new | past | comments | ask | show | jobs | submit login
Do We Create Type Systems In Dynamic Languages? (codethinked.com)
23 points by johns on April 6, 2009 | hide | past | favorite | 26 comments



What To Know Before Debating Type Systems: http://www.pphsg.org/cdsmith/types.html

Something about type systems just inspires people to use imprecise nomenclature and logical fallacies -- If people just referred to "Type-Checking" when that's what they mean instead of using "Typing" to describe everything, they wouldn't ask stupid questions like the title of this link.

Of course dynamically type-checked languages have a type system -- you're using it every time you do any operation in the language!


> We are all fully aware that if you are doing “kind_of?” checks all over the place then you are doing it wrong.

I think the author missed the point of what Alex Payne wrote:

> There’s lots of calls to Ruby’s kind_of? method, which asks, “Is this a kind of User object? Because that’s what we’re expecting. If we don’t get that, this is going to explode.”

They aren't checking types because they don't understand how to write polymorphic code or something along those lines. They are checking types as runtime assertions against programming errors; precisely the set of programming errors that static types prevent.

I've done this myself, when I found that parts of our code dealing with finances was mixing exact and floating point arithmetic, leading to occasional rounding errors. I built a simple DbC system for Ruby, and added type checks going in and out of various methods. With the contracts in place, it was easy to track down the location of the errors, and they stay in place as documention / protection for the future.


So, do you use this "DbC" in all of your Ruby code nowadays, like 95% of it, or in just where you think it's really important? And with this "DbC" do you think you could grow the Ruby code to 500,000 lines of code or more?


If you don't know what kind of object you're receiving and how it behaves, then there's something far worse going on than a runtime check.

Even if you do those checks temporarily, I don't think they should remain there, especially in production code.


It's easy to get cocky about this if you've only worked on relatively small projects in languages like Ruby, but being so cavalier with larger codebases will blow up in your face. Yes, code should be well-factored, but it isn't, so be careful.

I think type checks should absolutely remain in production code. (If you're considering removing them for efficiency purposes, measure if it matters.) The ambiguity that tends to creep in and cause nasty bugs isn't "is this a polygon or a string?" (I mean, duh), but rather, stuff like, "is this measurement currently in meters or millimeters?". Working in a dynamic language and putting checks in the few places where it really matters is good enough, but it really matters. It may help to think of type assertions as comments about expectations and intent that are automatically checked.

Also, languages with type inference can be a good compromise. You get the consistency checking of static typing without having to constantly remind the compiler that it's still dealing with an int or whatever. (I like OCaml, but it's not without its flaws. It takes a while to get the hang of working with, rather than against, its incredibly thorough type checking.)


Consider libraries in dynamic languages, there's no way to know beforehand the type of the argument a function is called with.


You mean there's something wrong with writing this many times a day in Perl?

if (defined($foo) and $foo->can('bar') and $foo->bar ne '')

I really relate to the article, because I deal with Perl 5 sucking all day long. Perl 6 has types. Moose has types. I can't use either, and so I end up writing really lengthy code doing ad hoc type checks all over the place.

Types are important to have when you need them, and you would move to using more and more of them on a more mature system like twitter at this point, but I prefer that they be optional. Moose objects and Perl 6 give me that option but don't require it, and thats nice.

Does Ruby do anything like that? Thinking about Ruby for my next project.


(If you're a startup CTO type, why are you doing Perl 5?)

Let's say you have InterestingObjects, a hash map from strings to objects. You could say InterestingObjects['foo'].andand['bar'].andand != '', but you might do better to rethink control flow, and reframe your problem, because there might be an easier way than always talking about variables that might not exist that have unknown properties. That looks like you're breaking encapsulation.


Perl 5 pays the bills :)

Actually, I don't have a problem with Perl 5/Catalyst http://www.catalystframework.org/ for a Startup, as long as you have several people quite good at it. If you know what you're doing, the main problem is finding good startupish Perl devs.

I confess that I don't understand your example. I'm not breaking encapsulation, I'm just verifying that a variable is defined (avoids 'variable not defined, can't call can' error), that it is an object with the method of interest (avoids 'no such method' error), etc.

In other words - I am doing a type check that its a valid object of the class I want with a non-null value on a field that should never be null. Happens all the time. This is more easily defined once in the class in a more reasonable object system. But Perl 5 without Moose has no such thing.

Which would seem to parallel the article. I could just assume that $object->foo will work, but that doesn't work out too well as code grows, because if I want my code to work or at least to easily be fixed, I have to check sanity everywhere possible.


I haven't tried, but I'm thinking about trying assertions for this based on the Smart::Comments module.

But maybe it's not a win and you never want to turn off (most?) assertions in dynamic languages.

(For non-Perl people, Smart::Comments are a neat way to e.g. allow runtime checks that are easy to turn off. Like C's assertions.)


I am an avid haXe user. It has static inferred typing with a "Dynamic" type, "untyped" blocks, and casting, all of which work more-or-less as advertised: the type system gets out of the way when you ask it to, and the rest of the time you get nearly-free type-checks. Sometimes it needs a little bit of annotation to help it along, but on the whole it's very handy for refactoring code.

In fact, I'm sure my iteration time goes up in haXe because I see most errors at compile-time. The ones I don't see are logic and uninitialized values.

ed: I mean time goes down, not up. Progress goes up.


I think it's just wrong to test for the type of the variable, unless it's really needed. Such checking detracts from what you are trying to accomplish and from relying on the exception backtrace to give you a clue of what went wrong when an error occurred. Also relevant is that if you are trying to pass in a new object that should work despite not being a direct descendent of a certain object, by checking for a certain type you restrict the usefulness of the API. Even "mocking" a certain object can be more troublesome then.

That said, whereas many Rubyists can make more use of respond_to?(), I can make a few uses of is_a?() every now and then. They usually make use of more idiomatic Ruby that way than me.

On the Spec/Test side, I hope everyone is testing more functionality than types as I am sure the type checking is just redundant in Ruby. Speaking of redundancies, a famous motto is all you need to follow to keep it cool: "don't repeat yourself".


Functions only work for a very specific set of input types. On all other inputs, something goes wrong, but that 'something' can take quite a bit of investigating to figure out. Therefore, an assertion that verifies that the input is indeed part of that specific set of input types is valuable: it catches the problem at the earliest possible moment and it pinpoints the exact problem.


For every other type of error, hah, rely on what I said then?


The more I have thought about fully dynamic languages that don't allow any form of static typing information, the more I have begun to think that they are not very useful. Sure, polymorphism is nice, overloading is nice, but all of the programs I have written, have relied on some sort of typing discipline, be it dynamic, static or whatever. I can't imagine writing a useful function for which I wouldn't know what it's type is. Just because Java, C++ etc. implement static typing poorly, doesn't mean static typing has to suck when there are many languages that have inferred static typing, which in my opinion can provide best of both worlds.

EDIT: By "best of both worlds" I mean "concise code and the benefits of the program being well typed"


What do you mean Java and C++ implement static typing poorly? C++ has some trade offs in order to be compatible with C. The Java static typing seems pretty good to me.


Maybe I should have said "they don't have type inference".


I think typesystems span far deeper than most object oriented typesystems go. In fact, I think, it goes even deeper than what haskell does (and I consider Haskells typesystem to be among the most advanced typesystems we have today).

One question for the typesystem is: If I have this object, will it understand message X?

Another question is: If I send this sequence of messages to this object, will the sequence of messages exiting the object be as expected? (Or, much rather: will a certain "interesting" subset of the messages sent by the object (triggered by my messages) be a certain sequence? (In fact, sequence is too restraining. I rather need to know if the messages sent by the object will fulfill a certain predicate)

Another question is: If I conceptually send this message to that object, how do I find the implementation of the handler of this message?

(I am sure that there are more complicated questions around and I just do not know them).

Given these questions, consider what most static type systems do. They answer questions 1). (Dispatch strategies and strong typing answer question 3) ) Guess what unit tests do? They answer question 2). However, in a dynamically typed language like Python, sending a message to an object that is unable to understand this message (read: there is no implementing method), then an unexpected message will be sent by the object (read: an Exception is thrown). This violates the contract. In other words: checking the contract can answer at least question 1) already!

Thus, I think -- or rather hope -- for major improvements at typesystems, because it appears that things are far, far more complicated than just compile-time vs run-time and strong vs weak. Consider Eiffel. Eiffel does contract checking. Now think about attaching contracts to objects at runtime. This could reduce the amount of of_kind?-checks in the article. Consider roles. Consider things like pythons metaclasses. I really think a lot of problems come from a lack of unterstanding object oriented typing more than we already do. (Maybe this kind of pushed me into a good direction for a master thesis. who knows?)


Not if you are doing it right. The article context is unit testing a dynamic languages vs a static language, saying that you have to do more testing with a static language. If you do thorough unit testing, the difference isn't all that great. The article implied that unit testing doesn't need to be as thorough with a staticly typed language, That is just plain wrong. You may need to do some additional testing with a dynamic language, but overall you come out way ahead. Python instead of Java, you come out way ahead. Python framework vs a full Java framework, the difference may be 5x or 10x.


The Python versus Java comparison is a red herring, I think. While Java is more verbose than Python, and some of that verbosity is from its type system, not all of it is.

A better comparison would probably be Python versus Scala, or Python versus OCaml.


It's a surprisingly common fallacy to equate Java with Type Systems.

Type systems can be far more expressive than what is available in Java, and can and should be used to enforce program correctness, reducing the necessity for additional testing.

Type inference, polymorphic typing, structural types -- the available tools in a comprehensive type system are woefully under-explored by most practitioners who consider Java to epitomize type systems.


True, but I speak of which I know. If someone has such a comparison, I would love to see it. The real measure is tokens not LOC.


Then your knowledge may be too constrained to derive sufficiently representative generalizations.

I can highly recommend:

http://www.xoltar.org/old_site/misc/static_typing_eckel.html...

The article (from 2003!) is an excellent treatise on the value of a proper type system as compared to Python, and moreover, how Java's type system (or C++, ...) should simply not be equated with "static typing".


Maybe. I tend to put it the other way around. Why would you need static typing if you're going to write unittests anyway.


A well-designed type system reduces the number of tests needed by allowing the programmer to enforce correctness through the type system itself.


Groovy allows you to specify types of method parameters, return values, fields and local variables. This was needed for Java integration, but has the benefit that you get runtime checks for types. So in Ruby you might do:

def foo(mystring): assert mystring type_of? String mystring.length end

In Groovy you could just do:

def foo(String mystring) { mystring.length }




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: