Irreducible Complexity



  • The great thing about higher order functions is that they bring us one step closer to the holy grail of ‘just say what you want done, let the compiler provide the implementation’. Take aggregation, for example: the ability to reduce any data structure down to a single value (think Max(), Min(), Sum(), etc) can be a godsend for those of us who value clarity and conciseness.

    TreeNode<TKey, TValue> Browse(TKey[] path)
    {
        return path.Aggregate(this, (node, key) => node.Child[key])
    }
    


  • Ok, I'll be the first to say it.

    I don't understand.



  • Do you mean you don't understand the code, or you don't understand why I thought it was a wtf? Because I took me ages to understand at first, but the code I was looking at was a lot more confusing (untyped dictionaries instead of trees, worse naming), so it could go either way.



  • OK, I got it. I was confused by mistype in code (missing ;) and missing class definition (also, a bit rusty in C#).

    It's doing path traversal using LINQ extension method.

    Meh. I like LINQ but this is maybe taking it too far. Not nearly enough flexibility to do the proper debugging or error handling.



  • BTW, here's the linqpad snippet I had to write to grok what this is doing.

    class TreeNode<TKey, TValue> {
    	public Dictionary<TKey, TreeNode<TKey, TValue>> Child { get; set; }
    	public TValue Value { get; set; }
    	
    	public TreeNode(TValue value) {
    		this.Value = value;
    		this.Child = new Dictionary<TKey, TreeNode<TKey, TValue>>();
    	}
    	
    	public TreeNode<TKey, TValue> Browse(TKey[] path) {
    		return path.Aggregate(this, (node, key) => node.Child[key]);
    	}
    }
    
    void Main() {
    	var path = new string[] {
    		"a", "b", "c"
    	};
    	
    	var tree = new TreeNode<string, int>(1);
    	tree.Child["a"] = new TreeNode<string, int>(2);
    	tree.Child["a"].Child["b"] = new TreeNode<string, int>(3);
    	tree.Child["a"].Child["b"].Child["c"] = new TreeNode<string, int>(4);
    	
    	Console.WriteLine(tree);
    	Console.WriteLine(tree.Browse(path));
    }
    

    The second WriteLine will put out the deepest TreeNode.


  • BINNED

    @cartman82 said:

    OK, I got it. I was confused by mistype in code (missing ;) and missing class definition (also, a bit rusty in C#).

    It's doing path traversal using LINQ extension method.

    Whew. Good. I was a bit worried for a minute there, thinking I'm missing something freaking obvious. But it's C# so I'm justified, since I don't know it. LINQ, even less.

    And no, I'm not fixing that smiley. I shall leave it there as a testament to Discourse being a dick.



  • Yeah, it just seemed like the strangest possible way to write it. My only guess as to how it ended up that way is that they would have written it out as a loop first, then refactored it to use linq because that's what's cool these days and who needs readability anyway?



  • Probably ReSharper.

    It's very good at detecting if/loop constructs that can be turned into an obtuse line of LINQ and then bugging you with its little widget until you click the damn thing.



  • And then you tell it to ignore that rule.



  • What's wrong with induction?



  • There's nothing wrong with it, in general, but this piece of code was one that made me wonder ‘wtf is even going on here?’ for far longer than I'd care to admit.



  • Fair enough. It looks like foldl wrapped up in some monad stuff.

    foldl :: (Foldable t) => (b -> a -> b) -> b -> t a -> b


  • Yeah, except its ass-backwards because you're folding over the path list, with the root of the tree given as the ‘base case’, and returning (presumably -- in fact that's another wtf right there: traversing a tree without ever checking if you're at -- ) a leaf.

    Functional programming can be great, but you have to be careful not to go too functional that you wrap around back to dysfunctional. For example If you start ever thinking that a linked list might be a good data structure, that's a warning sign, and the idea that's starting to form in my mind here is that if your base case for aggregation isn't the identity element of the function you're passing in, that should be another.


  • BINNED

    @Buddy said:

    Functional programming can be great, but you have to be careful not to go too functional that you wrap around back to dysfunctional. For example If you start ever thinking that a linked list might be a good data structure, that's a warning sign, and the idea that's starting to form in my mind here is that if your base case for aggregation isn't the identity element of the function you're passing in, that should be another.

    Examples would be helpful here. I'm not clear on why either of the two warning signs you mention would be more "functional" than alternatives.



  • This isn't a problem of the code being "too functional".

    This is a problem of code written in a functional style badly.

    @cartman82 said:

    Probably ReSharper. It's very good at detecting if/loop constructs that can be turned into an obtuse line of LINQ and then bugging you with its little widget until you click the damn thing.

    This.


Log in to reply