Hacker News new | ask | show | jobs
by latch 5534 days ago
The OP is down, but from reading the 1 comment, I'm sure this is a C# (possibly Java) developer.

1- Take a programmer who's tied to an IoC container and DI as a way of life, along with anti-singleton, and interface everything.

2- Introduce him or her to a dynamic language. Specifically focusing on testability without DI, interfaces and fear of singletons and statics.

3- Watch as he or she either:

a - accepts the fundamental truth that all that crap in a static language doesn't add value outside of freeing you from the language

b - refuses to believe that what you are doing can even be classified as programming

This is equally entertaining to do to either a very "experienced" (doing the same thing for the last 10 years) programmer, or someone who's just discovered mocking and mocks everything.

You can tell a lot about a Java or C# programmer by how readily he or she accepts this shift (which isn't to say they magically switch over to a dynamic language, but they should recognize that all that stuff a static language demands of us is really a limitation of said languages).

4 comments

Hm, I think your statements is too broad: "...the stuff a static language demands of us is really a limitation of said languages." You could apply it to some languages, like Java or C#, but the Haskell and ML folks have an entirely different view of static typing. As Haskell has proven, a good static typing system can make it easier to write short, clear code, correct code. And dynamic types are really just a kind of static types -- reduce a static type system until it has only one type, voila, a dynamic type system. I jest.

I tend to think about it the other way around, as "These are things I can do in a static type system but not in a dynamic one," but never let it be said that arguing about type systems on the internet was a good use of my time.

Would you care to elaborate a bit?

It seems your argument is basically: "HA HA look at the static language developers, aren't they stupid! They need to accept some fundamental truths and find their freedom!".

I'm interested in what you have to say but I'd like to actually hear some technical reasons to support your argument.

How do you test code that relies on the current time in C#/Java. I've seen a couple solution. Injecting some type of abstract Clock/SystemClock everywhere being common. C#'s lambda's let you do something nicer, like:

  class SystemTime
  {
    public Func<DateTime> Now => () = DateTime.Now;
  }
Ultimately though, you're having to work around the fact that Java and C# are class-oriented, rather than object-oriented. Classes are useful - they serve as a template for your object, but in most (all?) dynamic languages, the class itself is a living, breathing, modifiable, runtime object.

What does all this mean? How do you get the current time in a testable manner in Ruby? Use Time.now. You don't implement patterns or change your code. You don't fear the evil static..because really, it isn't a static - we don't even call it a static. It's just another method on another class, which we can rewrite at runtime as need be.

How do you stub it? Well, using a mocking framework:

Time.stub!(:now).and_return(Time.local(2011, 5, 3, 0, 1, 22))

The mechanics of how this is internally done isn't too complicated once you know about Ruby's metaprogramming (which probably takes around the same amount of time as getting familiar with C# or Java's reflection capabilities).

I know, it's a simple example. But it's surprisingly common. And I find it a little ridiculous that to get the time, we need to start creating interfaces and injecting parameters. While C#'s lambda's largely solve this problem, move to something just slightly more complicated, and you're back at square one.

I dunno, not sure if that explained it without being condescending. I'm tired...that stub is actually returning a pretty accurate time for me.

That's a nice explanation, without any of the bits I disliked from the original post, I appreciate it. :)

Essentially the benefit we're pushing here is cheap decoupling where in static languages you're having to set this kind of arduous plumbing up yourself, aye?

Right, with dynamic languages, decoupling is a language feature, not a design decision. It's one of the things that I feel make dynamic languages so much more productive. I work with large Java/C# applications. The loss of productivity due to dealing with traditional dependency handling, both at the implementation and at the test level, is significant.

I'm not going to say that it's as big a leap as automatic garbage collection (because, I don't think it is), but I see similarities between a C++ developer who doesn't understand why Java/C# don't have a malloc and delete method, and a Java/C# developer who doesn't understand why XYZ don't have interfaces nor use DI.

We spend so much time doing something a certain way, that it's hard to see how, given a different context (a new language) what we were trying so hard to avoid in the first place, just works.

Wait, I spend 99% of my time programming in dynamic languages, and I still use dependency injection. Why would I want to resort to hacks when I can just pass stuff in to my classes instead?

I will admit that I need the hacks from time to time, though. Recently, I wrote an overly large method that calls a callback when the command completes. For my test, I needed to run a bit of code right before the callback was called.

Runtime role application to the rescue! In my test, I wrote:

    my $done = AnyEvent->condvar;
    $done->begin;
    my $called_callback = 0;

    { pacakge FixupCallback;
      requires 'update_table';
      around 'update_table' => sub {
          my ($orig, $self, @args) = @_;
          my $cb = pop @args;

          # this is like "super"
          $self->$orig( @args, sub {
              $called_callback = 1;
              $cb->(@_);  # call the original callback
              $done->end; # then return control flow back to us
          });
      };
    }
Then, I just find the instance of the class that I need to tweak:

   my $view = $app->latency_publisher->view;
   FixupCallback->meta->apply($view);
And now I've fixed up the code enough to write a sane test. While cool, this doesn't negate the need for dependency injection. I want my app code to be 99% clean, and use the hacks for that 1% case where the hacks result in cleaner code than "the right way".

Also, I've never programmed Java or C#, so don't say that I have inherited bad habits from those languages :)

Singleton pattern overuse is pretty common among PHP developers, probably due to lack of namespaces (until recently).