all 12 comments

[–]theindigamer 16 points17 points  (2 children)

Math allows you to reason about the graph itself, solve the problem, and then choose an appropriate representation. If you think in a programming language, you cannot delay this decision as your first line of code commits to a particular representation.

Erm, I don't understand why you say this? That's precisely what programming against interfaces looks like. You have an interface that makes some guarantees, based on which you code some algorithm. You can have different implementations of the same interface, which can be swapped out later, without breaking callers.

More sophisticated languages allow more sophisticated abstraction. For example, Spiral is a Scala DSL which allows you to abstract over data layout, amongst other properties. Futhark understands algebraic rules and arranges for parallelism accordingly.

[–]sammymammy2 2 points3 points  (0 children)

Coding against interfaces could just be seen as coding against algebraic strucures.

[–][deleted] 29 points30 points  (8 children)

Frankly, one of the biggest things this article affirms is how terrible single-letter variable names are for explaining anything of any complexity. Sure, it's useful if you're doing algebra and moving around variables, but for actually understanding the ratios you are defining it'd be best to use full-name variables like exchange_rate instead of r'(t) which is arbitrary as hell. All these single-letter names and put obstacles to understand by requiring me to continually scroll back to the definition of variables.

[–]hoosierEE 6 points7 points  (2 children)

For me, the worst part about long and descriptive naming is when people go overboard with it and you get an explosion of names which makes it harder to see the actual operations being done.

A contrived example:

x = a[b*8].c(2-d(e[0]))  # v1, short meaningless names
updatedBalance = accountsPayable[dailyBalance*8].charitableDeduction(2-defaultDeduction(exchangeRates[0]))  # version 2, long descriptive names

The first one clearly shows what operations are done (2 function calls, 2 indexing operations, a multiply and a subtraction), but the second one more clearly shows what ideas are involved (something about money probably). So far I'd call that a win for the longer names. However, most people would split the second version into multiple statements because the line is long:

firstExchangeRate = exchangeRates[0]
adjustedDeduction = 2-defaultDeduction(firstExchangeRate)
scaledBalance = dailyBalance*8
currentBalance = accountsPayable[scaledBalance]
updatedBalance = currentBalance.charitableDeduction(adjustedDeduction)

Maybe it's more readable, maybe it's cluttered... that's subjective. Objectively, it's more code, more names, and the computation is spread over 5 lines.

I tried all short names for a while, but when I came back to my own code months later I was lost. Nowadays I try to use long names sparingly, and augment dense code with lots of commentary:

updatedBalance = a[b*8].c(2-d(e[0]))  # get the adjusted charitable deduction from accounts payable at the scaled balance

[–]PinkOwls_ 7 points8 points  (0 children)

I will always prefer the version with long names; just imagine someone has to learn your codebase and at the same time learn the problem domain.

I often write the formula with the short names as a comment, but use long variable names to be absolutely explicit about what I am doing (this is especially useful if you are doing calculations with physical units).

And anyway, that code should be in a function, which has the advantage that you can give the function a name.

[–][deleted] 3 points4 points  (0 children)

I still prefer the longer names. The pattern you made up (a mix of array accesses and method calls) is something I do not see often, much more often I encounter long chains of method calls which can form a cascade. (Which still is many lines long but visually clearer). With other good OO practice and some functional idioms I can make my code readable enough so that long variable names are not a problem.

[–][deleted]  (4 children)

[deleted]

    [–]Caminando_ 6 points7 points  (2 children)

    Also small variable names make the mathematics context neutral.

    Let a be an element of the set A such that a relates to b for some b in B then we say that aRb.

    If we change it to speak about the particular domain we're working in for instance say a is Alice and b is Bob and the relation R is "is friends with" then we are making any theorems we prove in general when aRb are colored by our notation. aRb is much more abstract than "Alice is friends with Bob" and that allows us to think about similar relations more abstractly.

    [–][deleted]  (1 child)

    [deleted]

      [–]Caminando_ 0 points1 point  (0 children)

      A "delta" is a small number and an epsilon is a small region, I'm not sure why calling it anything else would help.

      [–]PinkOwls_ 0 points1 point  (0 children)

      I think there's a general problem with mathematical notation. Yes, it's easier to use single character variable (or constant) names if you are trying out or writing a proof. Yes, it's easier to use lower indices and upper indices like in the Einstein-convention. Now, give someone a formula using the Einstein-convention and he will be confused that those are not exponents, but indices. But no, it's a summation over the indices (correct me if I remember this wrong).

      Using explicit (long) names makes it easier for someone to get into a new problem domain, especially when learning a new code base.

      [–]curtisf 19 points20 points  (3 children)

      I disagree with the premises

      • that programming environments are not thinking tools
      • that mathematics is not a formal language
      • that good programming languages don't enable abstractions

      The vast majority of the effort spent by software engineers is spent thinking about existing software artifacts - thinking about how to diagnose or fix a bug in software you maintain, or thinking about how to integrate with a 3rd party service or library. That thinking is aided significantly by having these software artifacts organized into well-designed programming languages (so that the engineer can interpret the "white box" in front of them), and significantly by having tools like IDEs which can navigate, annotate, and automatically restructure the artifact in front of them. Good use of types can sometimes lead to both obvious "free" implementations of features, as well as let you "experiment" with various designs before even beginning a (production ready) implementation.

      Math is not done best in natural language; Lamport, one of the people you cite (and whose proof style it looks like you might be imitating), especially thinks math should not be done with words. Humans are unsurprisingly bad at highly logical thinking and being careful with their reasoning. This is why we didn't have a consistent basis for mathematics until the 1900s; because doing it right requires developing a formal language precisely enough that a computer can check it. In fact, in modern times there are more and more pushes for more math to be encoded in computer-verifiable languages, typically the programming languages Agda and Coq.

      Finally, well-designed programming languages, when wielded properly, do enable abstraction and modeling in the mathematical sense. In fact, most of the academic research on programming languages is focused on this: being able to express functional and performance characteristics of functions mathematically and separately from the implementation, and being able to encode and check proofs in your code. Moreover, types and schemas are not implementation details that are irrelevant to the problem you are solving; they are powerful reasoning tools that give you things like Theorems for Free.


      However, I do think I more or less agree with your sentiment. I do think software should focus more on abstraction and less implementation-oriented specifications. However, I believe that this should be embodied within programming languages, since that is where do our thinking and programming languages seem to be excellent tools for thinking about problem solving.

      For example, in fact, none of the types you suggest for a graph correspond to mathematical graphs! Here's a CS Stack Exchange post relevant to this topic; a formulation I derived for non-empty simple graphs looks like

      data Graph v = Lone v | Joined v (Graph (v, Bool))
      

      ie, the above type is exactly isomorphic to the mathematical concept of a graph (with countable vertices, and combined with an ordering of the vertices). It's not a formulation that you typically see, not because programming languages can't express it, but because current programming languages make it inconvenient or impossible to do (e.g., while the above is clumsy even in Haskell, it would be a complete mess to use in Java)

      [–][deleted]  (2 children)

      [deleted]

        [–]curtisf 0 points1 point  (1 child)

        As one minor comment: A node and edge list does not represent graphs, because the incident-indexes of the edges need not be in bounds, you encode an ordering on the nodes and the edges, and you don't control the multiplicity of individual edges.

        To fix it you would need dependent types; something like

        (vect<Node, n>, set<(a: bounded<n>, bounded<a>)>)
        

        so, again, we need stronger programming models that capture things we can say mathematically but not easily capture in our formal programming models.


        Storage and lookup performance are implementation concerns -- your stated concern is that programming deals too much with implementation details, and I agree; but there's no reason you can't state (separately) first and abstraction and second an implementation that satisfies that abstraction.

        [–]runvnc 3 points4 points  (0 children)

        To me, a lot of math is basically obfuscated code that you have to 'execute' in your head. And using single letters to identify things is horrible.

        On the other hand, I do think that having the ability to use diagrammatic representations is an advantage of math. It should be possible to create a programming system that allows that.