Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

An abstraction is not useful, in fact it's downright harmful, if it does exactly the same thing as what it abstracts over, but more verbosely.

A lot of these patterns you're used to seeing in Java stem from Java's static nature.

Here's a car factory in javascript:

  function makeCar(dealerName) { return new Car(dealerName); }
And here's a configured car factory:

  var myCarProvider = makeCar.bind(null, "my dealer");
It makes precisely zero sense to create abstractions that reimplement core language features. That would be like writing a C function to add two uint8_ts, or a macro that expands to inline assembly to convert a float to an int32_t. You already have these things built-in, you don't need a gorillion enterprisey patterns.


This of course has nothing to do with staticness, and everything to do with Java's lack of first-class functions. In any flavor of ML, for example, nonzero-argument versions of this pattern are slightly cleaner (the exact pattern is unnecessary due to immutable state).

> or a macro that expands to inline assembly to convert a float to an int32_t

This sort of thing is actually useful on occasion, because it allows you to specify the exception handling you actually want (what to do on inf/-inf/NaN). Of course, it's better do it with a pure C function, but it's still a "reimplementation of a language feature".

Of course, you only ever do this when you want subtly different behavior than the language provides, so I think your larger point still stands.


Here's a car factory in javascript [...] And here's a configured car factory

Great, now where is the part where the code that provides the "my dealer" string can be completely unaware that makeCar() exists?

Oh, it doesn't exist? That's the problem that Angular DI is solving.


Here:

    var factory = makeCar;
The whole point of having first-class functions is that you can assign them. The problem a factory solves is precisely the lack of this assignability (which is why a constructor function is wrapped in a class).


Please put in the effort to understand at least the basic idea of dependency injection before deriding it in favor of "simpler" solutions that do not solve the same problem.

The specific pattern DI can solve here is:

- one module provides some capability by name

- another module makes use of that capability by name

- the two modules have no knowledge of each other, and no higher-level glue code is required to connect them together (and construct the appropriate objects at the right times).

Your "solution" does not satisfy the requirements because it requires you to manually construct the objects in the correct order. DI on the other hand will automatically analyze the graph of dependencies, invoking the appropriate factories in the right order.


Sorry to say, but klmr is right. Since functions are first-class objects and references are evaluated late, these issues are already managed by JS and native scopes. There's no need to recreate this functionality in a framework. You might just want to consider the entry point to your code.


P.S.: What this really is about: Unit tests originally designed to go with C/C++ do not work well with late binding. In fact this is a concept totally foreign to these languages. It's essential to understand that these kind of frameworks serve in the first line the purpose of test suites and only in second place the needs of developers. (Developers should not mistake themselves for test suites.)


You're completely missing the point and solving the wrong problem. Function or class, its a minor semantic difference for the same thing.

The issue is not how you define your factories, it's how you pass them around and use them in a component that has no knowledge of the outside world.


> it's how you pass them around and use them in a component that has no knowledge of the outside world

You pass it as a function argument.

    function needs_to_make_a_car(car_maker, road) {
      var car = car_maker()
      car.drive_on(road)
    }
To which you would probably reply: "But that's exactly what FactoryFactory/DependencyInjection/... pattern is!"

To which we would reply: "Exactly, which are just fancy words for the re-and-re-discovered concept of functional programming."


> To which you would probably reply: "But that's exactly what FactoryFactory/DependencyInjection/... pattern is!"

Actually no. DI does a lot that your simple examples do not. Notably, it understands the graph of dependencies between decoupled compoments and constructs provided values in the correct order.


DI is a design pattern. A design pattern is a conventional way to structure code to solve a particular type of problem. A way to structure code is not a thing that can analyze graphs.

Maybe you're talking about some particular DI framework. That would be a library with functions and classes to make implementing the DI pattern easier. There are many such frameworks, and some of those frameworks may well do automatic graph analysis. But, graph analysis is not inherent in the pattern.

Your specific framework is not the general pattern.


Except you don't touch on other aspects of what makes IoC containers useful in the real world.

I've seen XML configurability used as an extension point for code that would otherwise be closed. I've seen bugs happen because the lifecycle of objects have been mismanaged. You're conflating that things can be done more elegantly using functional programming to think it belongs in a functional paradigm.

IoC containers in .NET are easier to use and more elegant because of functional programming constructs, but their existence doesn't remove the need to have them.


> I've seen XML configurability used as an extension point for code that would otherwise be closed.

Take a step back, and think why that 'extension point' had to be closed to begin with. You're probably going to find either an architectural flaw, or a problem with your tooling that should not have required this sort of thing in the first place.

Also disturbing is that you are using XML to add a degree of expressiveness missing from whatever tooling you are using.


Hmm. So small example on what I saw recently was being able to swap the authorization/authentication piece from hitting a service to users defined in XML for locally running the project. Architecture flaw? No. Problem in tooling? If you have a convincing enough argument, yeah. Does Spring feel like it lets you do too much in XML? Hell yes. It's awesome when it works. It feels too convoluted when you don't need it.


> functional programming.

That's not functional programming.


i don't think you actually understand these patterns or how they're used. it's useful when you have multiple implementations of `Car` that you want to use under different circumstances. Or when `Car` is dependent on static configuration that might depend on the host it's running on, or might be different in development/testing/production.


I don't think you understand functional programming.

    function makeAppropriateCar(cargo) {
      if(cargo == "people") return new Automobile();
      else if(cargo == "boxes") return new Truck;
      else ...
    }

    function makeLocalCar(settings) {
      var car = new Car({'color': settings.get('new_car_color')});
      car.set_origin(settings.get_hostname());
      return car;
    }

    function makeDebuggingCar() {
      if(global_settings.DEBUG && !global_settings.PRODUCTION)
        return new Car({'loglevel': LOGLEVEL_DEBUG});
      else if(global_settings.DEBUG && global_settings.PRODUCTION) // testing
        return new Car({'loglevel': LOGLEVEL_INFO});
      else  // production
        return new Car({'loglevel': LOGLEVEL_WARNING});
    }


Ok, now try maintaining that when there are 15 types of cars and cars are used in dozens of services. You're imperatively expressing a dependency graph that you could (and would benefit from) defining declaratively at a larger scale.

I'd recommend reading the user guide for Guice for balance before you respond. It's worth seeing how nice DI can be.


What does your example have to do with functional programming?

You've written two factories, pretty much exactly how AngularJS uses them, in an imperative style.

Regardless of which, this doesn't in any way solve the issue Angular Services are designed to solve (IoC).


This example actually really sucks. Advocating for case statements shows that either you've never worked on a truly large project or you've never had to go back and modify your own code 2 years after the fact.

If you really understood functional programming, you'd see an if/case statement as an opportunity to replace conditional with a function.

I started to write up an example, but since we're talking about the imaginary abstract Vehicle implementation, it's not worth dignifying.

If programmers didn't switch jobs every 12-18 months and didn't see 100K LoC as a "large project", these "all frameworks suck" rants might turn into useful discussions of how one can organize code such that it's understood by more than the original author. That goes for frameworks too. If you have to rely on one of the core developers responding to a post on Google Groups or SO as your support mechanism, you've made the world less simple.


It's a fine example. Are you saying that 3 case statements is too many?

Why optimize the code to support cases that don't actually exist yet?

Or even better, why optimize the code in ignorance to how it will need to be optimized in 2 years?


None of these are examples of functional programming except that you wrote some functions.




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

Search: