Fear and Loathing in Programming La La Land (2012)(erlang.org)
erlang.org
Fear and Loathing in Programming La La Land (2012)
http://erlang.org/pipermail/erlang-questions/2012-March/065519.html
10 comments
There are legitimate reason to use create-set-call: for example, whenever you need polymorphism on object construction - ie, you're constructing a yet unknown type which implements an interface or extends a class.
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.
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.
What's wrong with:
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.I completely forgot about that pattern, not used C# in a while. Yes, this works for cases where you know what class you might pass in ahead of time, and can say new MyClass (although might break the principle of least astonishment). There are cases when you can't use that pattern though: for example, when you have some plugin framework, where plugins are loaded dynamically at runtime, you search for classes implementing an IPlugin interface, and create a new instance of said class.
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.
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.
In fairness, many API documents read like this:
Ref GetRef(); // gets the refIt's convenient that the poster initialized the relevant flame war this will degenerate into right in his post there. A discussion about the best way to structure APIs is surely not complete without flaming people about being elitist and that opportunistic programmers exist, and more flames about the fact that since they are producing lots of real world programs you should just shut up about them. I appreciate the guidance. I might have accidentally started flaming about event-based programming or the utility of NULL pointers instead, you know, and that would just be gauche.
>A discussion about the best way to structure APIs is surely not complete without flaming people about being elitist and that opportunistic programmers exist, and more flames about the fact that since they are producing lots of real world programs you should just shut up about them.
The 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.
The 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.
> The initial posting was structured to very much ignite a flame war
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.
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.
Now that discussion has passed, I'll point out that my post was basically an attempt to subtly immunize HN against the flamewar, disguised as a bit of humor. May have worked, we didn't have one, but who knows if we would have.
Richard O'Keefe has deep knowledge of computer science and software engineering, and his posts to erlang-questions, where you'll often encounter Erlang, Prolog, LISP, Java, and C neatly woven into the same message, are almost always enlightening.
He is the author of "The Craft Of Prolog", a quite famous book among people caring about programming.
[deleted]
Reading this, I though to myself that one of the biggest features I like in python that is missing in java is named and optional parameters.
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.
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.
I think optional parameters are a mis-feature in C#. You can acheive the same thing with a simple overload which calls a method without the optional parameter, and this is more robust.
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.
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.
Even worse, optional parameters are implemented backwards.
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.
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.
So you need to be mindful and have intelligent overloads for the DoSomething method?
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.
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.
Yeah, been through that before. We just standardised on using overloads for external APIs, it's cleaner externally.
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.
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 makes sense. As I said in my other thread, I locally deal with that problem by passing null instead of a primitive (though admittedly, I would probably use a primitive in cases of bool).
That said, in this circumstance my method sounds more like a hack and yours the cleaner, correct route.
That said, in this circumstance my method sounds more like a hack and yours the cleaner, correct route.
Having lived in both overload world and optional parameter world, I have found optional parameters to be infinitely more useful, especially when there are SEVERAL optional parameters that you may or may not want. Most of the time when I see the overload style, we have several:
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(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.They fixed this in F#.
> 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.
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.
[1]: http://en.wikipedia.org/wiki/Builder_pattern
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.
[1]: http://en.wikipedia.org/wiki/Builder_pattern
Needlessly condescending and not so bright himself for it.
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).
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).
I like readable code, he didn't mention readability. The only thing I really care about in code is that it is readable and doesn't stretch all in one line and not broken up. Small chunks are better than a ninja one liner.
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.
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.
FYI this was posted in Erlang's mailing list. There was a following discussion about it if anyone is interested.
http://erlang.org/pipermail/erlang-questions/2012-March/thre...
http://erlang.org/pipermail/erlang-questions/2012-March/thre...
From a purely technical point of view, doing this is a terrible idea as it adds additional, unnecessary state to objects, and hence makes it possible for them to break in more ways.
Using the Builder pattern, or keyword arguments, achieves the intended goal of making API features discoverable without the added complexity.
Using the Builder pattern, or keyword arguments, achieves the intended goal of making API features discoverable without the added complexity.
[deleted]
I don't know who Richard O'Keefe is but I do know he writes intelligent things on the haskell-cafe mailing list.
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
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.