News for hackers. Stuff that matters.
Re:On DRY and the cost of wrongful abstractions by MaulingMonkey on Sunday September 18, 2016 @06:32PM (#12527554)
> If you have a one-line function that is used one place (or even a few places) and it doesn't mean it is easier to understand just because you made it look like English. More likely you just obfuscated it: update_string_with_value(string,val) is NOT easier to understand than string.append(val).

We agree your sample has been obfuscated.

  /// <summary>
  /// Case Insensitive IRC Name
  /// </summary>
  public struct CIIrcName {
  	private string Data;
  
  	public static implicit operator CIIrcName( string s ) { return new CIIrcName() { Data=s }; }
  	public static implicit operator string( CIIrcName n ) { return n.Data; }
  
  	public string ToLower() { return (Data??"").ToLowerInvariant().Replace('{','[').Replace('}',']').Replace('|','\\'); } // Silly scandinavians :<
  
  	public static bool operator==( CIIrcName lhs, CIIrcName rhs ) { return lhs.ToLower() == rhs.ToLower(); }
  	public static bool operator!=( CIIrcName lhs, CIIrcName rhs ) { return lhs.ToLower() != rhs.ToLower(); }
  
  	public override bool Equals( object obj ) { return obj is CIIrcName && (CIIrcName)obj == this; }
  	public override int GetHashCode() { return ToLower().GetHashCode(); }
  	public override string ToString() { return Data; }
  }
Every single function of this is a one-liner. Are any of them obfuscated? (IRC servers sometimes talk about the same user or channel in multiple cases, this was a quick drop-in to prevent these from being mistaken as different users/channels by my IRC client. The "Silly scandinavians" comment references the fact that, yes, IRC thinks '[' is lowercase '{'. And yes, I've seen that matter in practice.)

Most of the changes I'd make here have nothing to do with method length:

1) Conversion (at least back to string) should probably be made explicit at this point (was originally implicit for ease-of-use when replacing string s)

2) ToLower could be made private (only used inside the type) and have the comment replaced with the above blurb (instead of being a comment to myself that only I'll properly decode.)

3) Rename CIIrcName to CaseInsensitiveIrcName?

4) Rename Data to OriginalCaseName?

> And you're also coding defensively against your fellow developers. This tends to be a problem in low- to average-skill workplaces.

My experience has been just the opposite. Low/average skill tends to mean less asserts, less static analysis, ignored warnings instead of warnings-as-errors, no unit tests, no thread safety annotations... forget my coworkers - I add these things to code defensively against future-me, and I've found it super effective on larger codebases.

> This goes all the way back to when they learned it was a good idea to comment their code "next = ++i // increment index then assign".

We agree this is bad too. A different perspective though - those are perfectly reasonable comments when you're new enough to programming that you don't remember what "++i" does! But it's something to grow out of and remove.

Re:On DRY and the cost of wrongful abstractions by InclinedPlane on Sunday September 18, 2016 @08:58PM (#12528090)
> "My experience has been just the opposite. Low/average skill tends to mean less asserts, less static analysis, ignored warnings instead of warnings-as-errors, no unit tests, no thread safety annotations... forget my coworkers - I add these things to code defensively against future-me, and I've found it super effective on larger codebases."

Bingo.

And this is another great reason to have more, smaller methods: it's far better for unit testing. When you have big, chunky function defs it can be a chore to figure out how to reach into the method and test one particular aspect of it. When your code is well composed into functions that are logically consistent, you can write unit tests that target each piece individually.

Re:On DRY and the cost of wrongful abstractions by jnordwick on Sunday September 18, 2016 @07:25PM (#12527734)
I can't really comment about your specific use case, but I would wonder why you are having to do these conversions and comparisons on the fly so much that you a full class dedicated to them and you don't have the the name normalized ahead of time. But I don't really know.

But like I've said in all my comments. There are times and places for all these maxims and rules to be broken. That's why programming is difficult, especially that is why finding the right abstraction so that it looks easy is difficult. The best programmers I know always seem to find that right balance and you look at their code and go "I could do that" when in reality you couldn't.

Re:On DRY and the cost of wrongful abstractions by MaulingMonkey on Sunday September 18, 2016 @08:30PM (#12527980)
> but I would wonder why you are having to do these conversions and comparisons on the fly

I receive a weird mix of normalized and unnormalized input over the network from servers and software I don't control, and want to keep the unnormalized versions for display purposes.

I could normalize on construction - might save a tiny bit of performance? - but then I'd need to store and keep in sync two separate strings (one for display, one for comparison.) Not that I have any real mutation going on that would violate that invariant in practice.

> so much that you a full class dedicated to them and you don't have the the name normalized ahead of time.

The general pattern is: Parse the network message (containing mixed normalization), then immediately lookup (or create) the target channel/user - allowing no duplicates due to mixed normalization.

With this I can simply construct e.g. Dictionary<CIIrcName,...>s and not need to remember to normalize every time I want to query by a given key. Only about 5 members that reference this type but there's a good 20-30 key lookups/equality comparisons using those members, handling different network messages among other things. That list will probably expand.

I could've wrapped dictionary access instead, but this seemed simpler (as dictionaries aren't the only thing I'm using, and none of the existing code wrapped dictionary access.)

Re:On DRY and the cost of wrongful abstractions by jnordwick on Sunday September 18, 2016 @10:07PM (#12528335)
I'd normalize on instantiation. It is both a performance win, smaller code, and much simplier. Chanel names don't change so you dont have to keep them in sync in any real sense, and if they do, the setter has a very trival job of setting both the real and normalized name.
Re:On DRY and the cost of wrongful abstractions by unscaled on Monday September 19, 2016 @09:55AM (#12531458)
The nice thing with wrapping it in an abstraction, is that you can change or add optimizations without having to change the code EVERYWHERE.