News for hackers. Stuff that matters.
Re:Code Smells: Iteration by hinkley on Wednesday August 23, 2017 @11:37AM (#15082342)
It's worse than that. We have met the Enemy and the Enemy is us.

Java, as introduced, claimed that nearly everything was an Object. What we got instead was nearly everything is a String.

The Real WTF in this code is that all of the important information is passed around as Strings. The iterator and its source hint at this but she fixes the wrong problem. Ever has this been the way with Java. Despite having a statically typed language nobody turns the data into information and instead your modules just vomit strings at each other. Sometimes in groups of four or more.

Re:Code Smells: Iteration by chriswarbo on Wednesday August 23, 2017 @12:46PM (#15083003)
One reason for this is the tight coupling in developers' minds between what I'd call "types" and "representations" (these terms are very overloaded, so others may use them in different ways).

Just because, say, a function name and a string are represented in memory the same way, that doesn't mean they are the same type; in particular there are many strings which aren't function names, and there are many operations (e.g. append) which make sense for strings but not for function names.

I can think of two things to blame for this:

- Languages which make it complicated, verbose and/or inefficient to introduce new, incompatible names for existing representations (i.e. nominal typing). For example, having to declare a new class in a new file with a private field, a constructor method and a getter method; that's a lot of boilerplate, and those method calls will incur a runtime cost. Haskell's `newtype` is pretty good in comparison, e.g. saying `newtype FunctionName = FN String` lets me use `FunctionName` in type signatures, I'll get a type error if I try to use a `String` as a `FunctionName` or vice versa, and I can construct and destruct a `FunctionName` using `FN` (e.g. `let x = FN "foo"`)

- Developers only coding for the happy path. When writing a test suite, it's important to test that bad outcomes are prevented, as well as just testing known-safe inputs. The same should apply to types: types should be used such that meaningless expressions are ill-typed, as well as just allowing known-good expressions to be well-typed.

Unfortunately these sorts of considerations tend to get derailed by similar-but-unrelated issues, e.g. 'YAGN a `FunctionName` interface because there's only one implementation'.

The end result of all this is Web applications concatenating together a bunch of "strings" which are actually HTML, plaintext, SQL, user input and URL parameters :(

Re:Code Smells: Iteration by msla on Wednesday August 23, 2017 @02:44PM (#15084007)
You can go further with the type/representation dichotomy.

An obvious example is printing: All of a sudden, you need to convert an object of that type to a representation that can be read. Read by what? Humans? Other code? Both to some extent? The representation is dependent upon both the original type and the intended recipient.

(In Lisp, "readable" means "acceptable to the read function, which parses Lisp expressions"; some objects, such as compiled functions, inherently cannot become "readable" in this sense, so they get printed in an unreadable form. Should that be a type error?)

You can even have layers of representation: Length is a type of value, whether it's expressed in inches or centimeters or light-seconds is a representation, and whether it's in ints or floats or strings is another layer to the representation. You can add inches to centimeters with the right conversion, much like you can add numerical values represented ints and strings with the right conversions. The conversions just have to be at the right layer of representation.

Your ideas sound a lot like the original Hungarian notation, BTW: If your language-level types are representations (as in, your type system says int and float, as opposed to semantic notions like pixels-from-edge or alpha-percentage) you can encode the real type information in variable names. People mutilated this to encoding language-level type information in variable names, which is utterly pointless and potentially harmful.

Re:Code Smells: Iteration by chriswarbo on Thursday August 24, 2017 @07:41AM (#15089240)
Regarding things like serialising/deserialising, I don't think there's much issue 'philosophically': in the case of Lisp's `read`/`write`, we can spot the ambiguity and deal with it by writing separate functions, for example:

- `serialise`/`deserialise` to produce/consume machine-readable data; must be mutually inverse (which rules out your example of functions).

- `pretty-print` for human consumption; has no inverse.

As a MVP, these could just be wrappers around `read` and `write` (macros would prevent any runtime overhead); the names convey the intended meaning. Later on we could start enforcing some checks, but since Lisp is dynamically typed, we'd have to do runtime tag-checking (e.g. "if type of 'X' is 'function', throw an error"). A static type system/checker would be better.

More generally, each application should be responsible for parsing its input into a domain-specific model, with machine-checked types. A pair of communicating applications may choose to delegate that responsibility to some common library, but they should not assume that their input can be trusted since it's 'coming from that other system'; e.g. they shouldn't pass around a `String` of input as if it were a domain object, and mangle it by pull out particular characters, concatenating things, etc. That `String` should be parsed into its components ASAP, and those should be passed around.

You're right that "apps hungarian" is another possible approach: slightly better than just documentation, but still not manchine-checked. I think the history of apps hungarian degenerating into systems hungarian is another example of this type/representation conflation.

Re:Code Smells: Iteration by tXEHOKhd on Wednesday August 23, 2017 @01:59PM (#15083652)
It's not just Java! I see this all the time in C and C++ as well. Developers seem to be afraid to use types as they were intended. For example, look up any OpenGL question on StackOverflow and you'll see things like what should be an array of 3D vertices with separate x, y, and z components passed as a 1D array of floats.

In C++ we see quite a bit of abuse of std::pair<> instead of just making a fucking struct to hold 2 properly named, easy to read pieces of useful data. Nope! Apparently calling all your data members "first" and "second" is better to some people.

I don't know if it's an educational problem, a language issue, or something else, but I see it so frequently that there must be some common underlying cause (or set of causes).

Re:Code Smells: Iteration by leeter on Wednesday August 23, 2017 @02:44PM (#15084019)
It's an educational problem, Chandler Caruth of LLVM gave a really good talk on POD type slicing and how useful it can be. More to the point that for PODs it's supported in the standard. I do agree on the abuse of pair... pair is there for implementers primarily and shouldn't be used outside of deliberately templated code. Ditto tuple.

That said... with C++17 destructuring... it's a lot less painful since you can do the equivalent to a std::tie in a single line.

Re:Code Smells: Iteration by apta on Friday August 25, 2017 @07:33AM (#15097672)
What was the title of the talk?
Re:Code Smells: Iteration by throwaway613834 on Wednesday August 23, 2017 @03:04PM (#15084192)
One problem in C++ is if you define a struct then you have to define a bunch of operators yourself (operator<, etc.) which gets annoying after you've realized you've needed them a bunch of times.
Re:Code Smells: Iteration by marcosdumay on Wednesday August 23, 2017 @04:13PM (#15084638)
Does C++ Pair<> define operator< and operator>?

That's surprising, and for me looks completely uncalled for.

EDIT: Just looked into Haskell (because it's easier), and tuples do derive Ord there. Is there some universal convention I'm missing?

Re:Code Smells: Iteration by throwaway613834 on Wednesday August 23, 2017 @05:34PM (#15085394)
> Is there some universal convention I'm missing?

Tuples are always compared lexicographically, is the universal convention. Not sure if that's what you might be missing?

Re:Code Smells: Iteration by khedoros1 on Wednesday August 23, 2017 @05:37PM (#15085421)
> Does C++ Pair<> define operator< and operator>?

It does.

std::sort(pairarray.begin(),pairarray.end()) works about how you'd expect, as long as operator< is defined for the constituent types of the pair.

Re:Code Smells: Iteration by nickbauman on Wednesday August 23, 2017 @11:47AM (#15082446)
Agree. I think we're all Stringly-typed developers. We use strings everywhere. We throw strings across system boundaries at will.
Re:Code Smells: Iteration by seanp2k2 on Wednesday August 23, 2017 @12:40PM (#15082957)
If you're just using objects to represent groupings of strings and encapsulate behavior around converting them back to strings for output at some point, why not just operate on the strings instead of all the serialization / deserialization? Maybe enough of the quick stuff I do isn't enough to warrant more complex data structures, but I get pretty far into a problem with just dicts (in Python) and generally get annoyed when something returns back objects that have no easy str representation without finagling the object into giving me what I need. I think that this is generally a bigger issue with libraries and treating object metadata (stuff like time stamps, extra attributes, etc) as of equal importance as the data it represents; if an object has one obvious attribute for what the thing is, make that the default thing you get when you just refer to the thing, instead of making everyone call some weird method to turn the instance into the representation that you likely want to get out of it.
Re:Code Smells: Iteration by nickbauman on Wednesday August 23, 2017 @07:19PM (#15086079)
This is one of the major reasons why I love Lisp so much. Serialization problems disappear in homoiconic languages. It eliminates an entire class of issues.
Re:Code Smells: Iteration by cormacrelf on Wednesday August 23, 2017 @12:55PM (#15083095)
cat your_comment.txt | fold -w 80 -s | grep that | head -n 1 | sed "s/ have/'d be/" | sed 's/n/to/' | cut -d's' -f1-2
Re:Code Smells: Iteration by msla on Wednesday August 23, 2017 @02:46PM (#15084035)
If you're going to be snarky, fix the useless use of cat.
Re:Code Smells: Iteration by hinkley on Wednesday August 23, 2017 @12:20PM (#15082779)
The first time I ran into this was on a project that was passing contact information around like String, String, String, int. Back and forth across the system in four or five workflows with extra data passed in here and there.

Of course they left off Address Line 2 and rather than try to fix this in thirty places as I was told I made an Address object instead.

With very rare exception, a street Address (Address 1) is meaningless without at least the zip code. And god help us if you had two addresses.

Later it took someone a couple hours to fix it for 9 digit zip codes. No further changes unless a new State joins the union (this was for a state court system and they required a domestic mailing address. One of the few times I didn't eventually need to process international addresses)

Re:Code Smells: Iteration by jerf on Wednesday August 23, 2017 @12:48PM (#15083031)
"We use strings everywhere."

That's bad.

"We throw strings across system boundaries at will."

That's neither good nor bad, it's just inevitable. The point of not using "strings" is that strings generally do not have the semantics of whatever it is you are really dealing with, because strings are just raw sequences of bytes and are generally too permissive for the specific type. For instance, if you have a URI, that comes with a list of restrictions for validity such as "can't start with a colon". Programming-language strings aren't URLs because they permit starting with invalid scheme declarations, or being empty.

However, when crossing system boundaries, you are exposed in some sense to the raw truth of the hardware, which is "raw sequences of bytes" is the only thing that really exists. When someone sends you something claimed to be a "URI", you really can't blindly count on that, if you want robust code you are going to have to validate that. Even if you work very hard and use higher-level abstractions to try to map things to higher-level types, you still have the problem that the semantics have to be an exact match for this to work; in one program the URI type only permits http and https, in another it permits arbitrary (valid) schemes, and there you go, there's a difference sufficient that you must validate.

To match semantics between two programs sufficiently that you can claim with a straight face that you really, truly aren't throwing "strings" across system boundaries requires incredibly tight coupling between all participating programs.

Re:Code Smells: Iteration by nickbauman on Wednesday August 23, 2017 @07:21PM (#15086089)
Tight coupling ... or just s-expressions ;)
Re:Code Smells: Iteration by jerf on Thursday August 24, 2017 @09:42AM (#15090019)
S-expressions are irrelevant to my point. S-expressions are just a serialization format; the use of S-expressions does nothing to synchronize semantics between two programs. Labeling a string as a "uri" with an s-expression does nothing to solve the problem I mentioned about mismatched semantics between two programs. In fact, it does nothing to solve (uri "::::::////SUPERINVALID"). Behold the fundamental problem with semantics and why the semantic web has never worked out as its advocates hoped; labeling is the easy part of the problem.
Re:Code Smells: Iteration by nickbauman on Thursday August 24, 2017 @05:28PM (#15094214)
S-expressions are much more than a serialization format.
Re:Code Smells: Iteration by jayd16 on Wednesday August 23, 2017 @11:50AM (#15082476)
Just FYI, they ran this example over some built in reflection APIs. Hard to write code designed to get methods by string name without strings...
Re:Code Smells: Iteration by hinkley on Wednesday August 23, 2017 @12:12PM (#15082699)
Exhibit A:

    The getLoadNames method returns a List of Strings, which we iterate over in order to see if a particular String value is in there. 

Even in describing the algorithm we aren't informed it's a list of function names.

Edit: from the comments in the code it's pulling field names from a Mongo result set, so it's member variables more so than function names. One half of that relationship is still deterministic at runtime. Mongo records can change whenever but your ORM is dealing with Classes and their contents are known at runtime.

Re:Code Smells: Iteration by s73ver_ on Wednesday August 23, 2017 @12:33PM (#15082894)
Because for the purposes of example, it doesn't matter what the data actually is.
Re:Code Smells: Iteration by hinkley on Wednesday August 23, 2017 @12:40PM (#15082958)
I'll bet you a six pack that hasName is called in a tight loop somewhere. The cost of generating the collection on every call is far greater than the cost of iterating it was.

Can we at least agree that the chosen example has too many other confounding factors in it?

Re:Code Smells: Iteration by recursive on Wednesday August 23, 2017 @11:54AM (#15082512)
Unix started it.
Re:Code Smells: Iteration by hedora on Wednesday August 23, 2017 @12:14PM (#15082726)
Even C has types, and the UNIX API's make heavy use of them.

Awk and sed and grep and friends certainly encouraged strings, but I heard there was an even earlier thing called REPL. From what I've read it is not a coincidence that PERL is an anagram of it.

Re:Code Smells: Iteration by jnordwick on Thursday August 24, 2017 @12:54AM (#15087680)
Linux /proc and /sys were the epicenter