Fear and Loathing in Programming La La Land (2012)
erlang.org
erlang.org
1) Hmm, let's see what classes are in this namespace... Ah, this one looks like it might do the job so I'll type
var foo = new ThingWithInterestingName();
2) Dang, don't want to scroll through tooltips for 23 different constructors with poorly named params, think I'll just use the default constructor, they probably implemented that for casual users like me.3) Now I can type foo. and let IntelliSense give me options. When I call a function I get an error for each call instead of some crazy exception for the whole thing. Maybe I can even test stuff out in the Immediate window in the debugger.
So, I think it's not only miseducation or layzness, it's that modern IDEs for static languages encourage discovery based development in favor of reading through API documents.
C# doesn't allow you to specify in an interface or abstract class the parameters that a constructor must take: which makes it very difficult to use such classes as generic type arguments for instance. There's a new() constraint which allows you to call a parameterless constructor, and if you also constrain the generic type argument to some interface, you can set the data you want through it.
If you expect parameters in the constructor though, you can't simply create a new object of the type passed as a generic argument. You must use reflection (Activator.CreateInstance(typeof(T) ...), assuming that there exists a constructor for the yet unknown type (obviously not statically checkable). To prove such a constructor exists at runtime, you can use typeof(T).GetConstructors(), and search through the returned collection for a ConstructorInfo with matching arguments. If you get this far, you'll probably drop the idea and resort to the create-set-call mess instead.
I suppose a way of fixing this would be to introduce some kind of "constructor pattern" into interfaces and non-sealed classes, requiring subclasses to implement it and allowing the use of said constructor pattern as a generic type argument constraint.
Another time you might need create-set-call is if constructing objects which have a mutual reference to eachother. [ var a = new A(); var b = new B(a); a.setB(b); ] cannot be expressed with parameters in both ctors. This might seem like a "bad idea" due to the circular reference, but it's more common than you'd think, and doesn't necessarily mean bad code.
void Foo<T>(Func<Int, String, T> makeT) where T : IIface {
T myT = makeT(10, "hi");
// ...
}
Foo((x, y) => new MyClass(x, y));
In patternese, this is injecting a factory dependency.Reflection must be used in that case. Using `new T()` for parameterless ctors is just a call to Activator.CreateInstance<T>(), but there are no generic CreateInstance overloads taking arguments.
Ref GetRef(); // gets the refThe initial posting was structured to very much ignite a flame war, already from the title ("programming la-la land").
It was arrongant, condescending, and, as far as it's content is concerned, shallow.
Basically merely a few quick remarks that were supposed to take down a proper study (of which the "critic" misunderstood the scope, reasoning, and who it concerns).
As for the existence of "opportunistic coders" (a concept which he also misunderstood), he wasn't told to "just shut up about them", but to understand that whatever the fuck his objections are, they are a real group, they are valuable, and an organization operating in real life needs to also catter for them.
If that is indeed true, you will notice from the replies to his message that he definitely did not succeed. However, I prefer to interpret it at face value. He does say "Frankly, I've turned here for a bit of comfort. If anyone likes completely initialising things rather than smacking one field at a time, surely I will find such people here.". To me it is just venting amongst friends.
Also, any api/library that lets you create an object that is unusable because specific methods to set up values haven't been called yet is very error prone.
The problem with optional parameters is they work by call site rewriting. If I have some library method where I give an optional parameter a default value, and a program is built against it - if I later change the default value, this will not be propagated to the program until it's recompiled - it'll continue to use the old default.
They could probably fix this by implementing overloads behind the scenes, rather than rewriting the call site though. If Java does ever implement this, I hope they do it that way.
public void A() { A(1); }
public void A(a) { A(a, 2); }
public void A(a, b) { A(a, b, c); }
public void A(a, b, c) { /* do something */ }
Or, worse, public void A() { A(1, 2, 3); }
public void A(a) { A(a, 2, 3); }
public void A(a, b) { A(a, b, 3); }
public void A(a, b, c) { /* do something */ }
It wastes several lines and could be much more simply expressed, to the developer as: public void A(a = 1, b = 2, c = 3) { /* do something */ }
Regarding the issue you have, I can certainly see that as an actual issue and it should probably be dealt with; though, the CLR developers probably had to decide between two opposing and frustrating viewpoints: "What if they want the old default? After all, they did compile against it, knowing that that's what we set it to default"; and, "Maybe they just want the computer to figure out what's best for them..." As a library designer, you'll now have to consider the possibility that they're using the old version of your library. Realistically, they most likely will compile their program again instead of in-place replacing your dll. It's safer that way.All of that said, when I usually use optional parameters, I generally set them to null. You cannot set them to, for example, a new instance of an object, they have to be static values; so, if someone uses a default parameter, my code sees: "oh, it's null. Now I'll use my default parameter that I set internally, because it came from a configuration file and I couldn't set that as a default parameter anyway given their constraints", and I don't have the problem you mention at all.
Example:
public void A(a = null, b = null, c = null) {
a = a ?? new AParam();
b = b ?? SomePropertyFromAConfigFile;
c = c ?? DBNull.Value;
/* do something */
}
The ?? syntax makes this practically the recommended route, I think. (a ?? b is C# shorthand for (a != null? a : b) ). I'm happy, the people using my library are happy, and the issue that you describe doesn't exist in my code because of this. Of course it does make it a bit awkward for some kinds of primitives, for example; but, if they don't want to provide a parameter to my library, that parameter being provided as 'null' seems the most accurate way for my library to receive it anyway.That is: public void DoSomething(string required, string optional = null) is actually DoSomething(string, string), but at compile time a call to DoSomething("foo") is compiled to DoSomething("foo", null).
Hence optional parameters are not recommended for an external API.
Personally, I don't really see that as much of a problem. Having run into that issue before, I think the compiler catches it as as an error because its an ambiguous method signature. Instead, you have to pass something like: "DoSomething("foo", (string)null)" which, in that case, defeats the purpose of the optional parameter altogether.
The real issue is if you have DoSomething(string what, autoRetry=true) and you change to DoSomething(string what, autoRetry=false) - DLLs compiled against the old version will call with true even if the authors wanted the default behaviour.
That said, in this circumstance my method sounds more like a hack and yours the cleaner, correct route.
That's what the Builder pattern[1] is for, avoiding the instantiation of incomplete objects.
I've thought about the appropriateness of that pattern to Python, and so far what I've come up with is merely to use optional named parameters for private/simple classes and Builder classes for public/brittle/robust classes/APIs.
For example he didn't understand what the three categories of programmers were meant to be (tendencies between good programmers working at MS -- hence, even those deemed "opportunistic" are not some clueless idiots worthy for the Daily WTF).
Second, there are a few cases to be made for the second style (even over named parameters).
One is less clutter at the constructor level.
A second is easier reflection/dependency injection.
A third is it forces saner defaults.
Also, better readability for some (which is not all about cramming as many lines in the same editor screen).
It also makes parameters independent of ordering -- which in the compiler they are, but in a constructor they are not (except with named parameters, and even those have to come AFTER non named ones).
The only real downsides are: a) the possibility of using a half-baked object. But I don't see how that would be any different than omitting some of the named params. b) third parties mutating the objects. But then you have the same "issue" in most dynamic languages anyway.
(The off topic jab against "climate change" at the end was also pathetic -- apparently he's one of the climate change deniers, despite overwhelming acceptance of the notion from the scientific community, putting them on par with Creationists).
Constructor arguments/params are fine as long as the code is readable and line breaks to keep it within a decent column size. I also think that constructor arguments can sometimes get into overload messes just as method calls can. A good api breaks up any overly parameterized constructor or methods.
I just want code to be readable, fit within a decent column area and make sense. Whether it is in constructor params or method params there shouldn't be too much done in one line for complexity sake.
If programmers don't get constructor params then they probably don't get method params either. I think people just want stepped-readable code in most cases. Named and optional params help to clear up long initialization or method calls but in the end it is about balance and readability.
Smaller, iterative code bits are easier to put a breakpoint on as well say for instance one param was null and it broke on constructor, it might take some more inspection to find unless it was a specific set.
http://erlang.org/pipermail/erlang-questions/2012-March/thre...
Using the Builder pattern, or keyword arguments, achieves the intended goal of making API features discoverable without the added complexity.