Live data from Hacker News

Write code. Not too much. Mostly functions.

brandons.me

321–330 of 362 posts

Re: Write code. Not too much. Mostly functions.

#321

> Bad programmers worry about the code. Good programmers worry about data structures and their relationships. — Linus Torvalds

That remark from L. Torvalds is, I think, about how to approach problem solving. I'm sure he does have some opinions about code style and organization, but I feel that they would be shelved under a different conversation.

Programming is ultimately about reading and transforming data. When presented a problem, the bad programmer (presumably not well versed in DS theory) thinks of it first in terms of "steps to reach a solution". Whereas the more skilled programmer is able to identify which set of DS is a good match to a specific problem. That is, which combination of structures allow for an efficient access and manipulation of the data in the context of the problem at hand. The implementation then stems from that insight.

Obviously this skill is mostly valuable in performance critical code, which the bulk of our trade generally doesn't intersect with. And in an age of fast processors and abundant memory, it's common to see O(N) data structures applied to O(1) problems and barely anyone notices the cost. Thus keeping us in the comforting illusion that we're better programmers than we actually are.

Re: Write code. Not too much. Mostly functions.

#322

What's the difference between writing OO code that depends on internal state and writing a pure function that expects an argument that is a data structure of a specific type (and thus has internal data that could be different)? Is the pure function no longer pure if the argument is a data structure thats complex and the values within the data structure dictate the outcome of the the function? Or is it a pure function…

Purity is a question of mutability, nothing more. If the function mutates its arguments (or its closure, or its global environment), it is impure. Any useful program will of course need to do these things at some point, but there's a lot of logic that just goes from A -> B (or A, B, C -> D, or whatever), that doesn't need to concern itself with these things, and should be insulated from them. There's nothing inherently impure about taking large data structures as arguments, though it does make it trickier to enforce immutability in most languages (compared to primitive values).

It's worth noting that it's entirely possible to write "pure methods". Unfortunately most languages don't really let you a) have mutable structures, and b) write enforced-immutable methods on them. Rust is the only one I know of: a method can take a &self instead of a &mut self, which prevents it from mutating self (recursively, which requires knowledge about ownership unless your language is 100% immutable like Haskell or Clojure, which is why this feature is so rare). What I tend to do in other languages like JavaScript, C#, or Python is to use property-getters as a convention that strongly suggests purity; unfortunately that's about the best you can do.

In multi-paradigm languages the decision on whether to make something a pure "getter" method or a standalone function is mostly one of aesthetics. Standalone functions give you a bit more flexibility in use, but sometimes the foo.prop syntax is more readable.

Re: Write code. Not too much. Mostly functions.

#323

Earlier quoted context omitted.

The bold utilitarian approach of Go might face some valid criticisms from seasoned programmers, I myself had to empty my cup(mostly Java) to get onboard Go and I'm glad that I did. After a spine surgery my programming time got severely limited and so I decided to code my future projects with utility focused languages. I had used Python in the past, but the performance tuning once the application scales is counterprod…

How do you feel about coding without generics now, and what do you think about Go's ambition to add them? I'm coming from a C# mindset and thinking of learning Go, but I'm so used to generics...

That's what I meant when I said that I had to empty my cup and It's unnecessary for most if they're happy with their current language.

As for the inclusion of Generics I'm divided, I'm eager to use generics again in my current Go to language but on the other hand I'm worried if this is the direction Go language design team is going to take then where will it end?

Re: Write code. Not too much. Mostly functions.

#324

Earlier quoted context omitted.

> My point is, that the use of combinators and point free programming formally eliminates organizational technical debt. Hardly. It may eliminate certain kinds of technical debt. Pretty sure it won't eliminate all of it. As you said in a parallel post: > Readability is definitely worse when using this method. Well, that's a kind of technical debt.

Read my post: I said "organizational technical debt" to specify debt that has to do with how you organized your logic.

BTW, the term "organizational" usually refers to how you organize your people.

Even under your definition, though, point-free only helps with a limited definition of "how you organize your logic". How do you organize it into layers? How do you organize it into files? How do you organize it into processes? How do you organize it across machines? You can get real "organizational technical debt" on all of those.

Re: Write code. Not too much. Mostly functions.

#325

Earlier quoted context omitted.

"I've seen too many of my previous projects die right when I moved on. Now I tend to write code as if it were written by a beginner: verbose and boring, with no magic." Theres nothing wrong with charming magic in your code, if it really does something special and is not just used for the sake of it - it only gets into dark magic, when you forget or are too lazy to add proper documentation in the end. Which ... happen…

I once read a quote, possibly here on HN that said: "Code first for the machines, then for others that will maintain your code and lastly for yourself." And that I think for me nicely strikes the balance.

Hm, coding for the machine would mean to me, write processoroptimized code allways.

And I rather have clear, maintainable code - which is easier to work with and therefore less filled with bugs.

Re: Write code. Not too much. Mostly functions.

#326

Earlier quoted context omitted.

I've been playing with Joy (concatinative, combinator-based) and I have to agree. So far everything I've translated into Joy has been more elegant and easier to understand. Conal Elliott's "Compiling to categories" is one way in: http://conal.net/papers/compiling-to-categories/

Any paradigm can look clean when you’re playing. Work on it for a year and then have management tell you that we must add a feature that breaks your core design because our big client needs it.

Kind of a pointless objection. Any car is slow if it hits a wall?

Re: Write code. Not too much. Mostly functions.

#327
post #302

Earlier quoted context omitted.

The problem with currying is that I don't know of a nice syntax to curry on a random access argument rather than on the first one.

It exists with dummy variables. Ive seen it with C++ bind and many fp libraries in JavaScript as well.

But then it's not pointfree, it's just anaonymous variables instead of named.

Re: Write code. Not too much. Mostly functions.

#328

What's the difference between writing OO code that depends on internal state and writing a pure function that expects an argument that is a data structure of a specific type (and thus has internal data that could be different)? Is the pure function no longer pure if the argument is a data structure thats complex and the values within the data structure dictate the outcome of the the function? Or is it a pure function…

OOP is merely a way to arrange your code. There's no magic sauce, it's just topological distortion without deep semantic significance. In other words, there isn't really a difference. Maybe the code is easier to understand in the OOP style, maybe not, but that's in one's head.

Re: Write code. Not too much. Mostly functions.

#329
post #126

Earlier quoted context omitted.

Problem domain = Shopping, Banking, Coffee Shop, Factory, Airplane Dimension = Customers, Accounts, Users, Widgets, Inventory Higher-Order Logic = combining basic functional building blocks in order to compose more complex functionality. SQL enables direct, declarative access to the whole space of higher-order functions. E.g. You want the list of widgets made 3 quarters ago but scoped to one factory line, and only wh…

Thanks. That is clear.

BTW all those terms are like a century old.

Re: Write code. Not too much. Mostly functions.

#330

Earlier quoted context omitted.

Read my post: I said "organizational technical debt" to specify debt that has to do with how you organized your logic.

BTW, the term "organizational" usually refers to how you organize your people . Even under your definition, though, point-free only helps with a limited definition of "how you organize your logic". How do you organize it into layers? How do you organize it into files? How do you organize it into processes? How do you organize it across machines? You can get real "organizational technical debt" on all of those.

> BTW, the term "organizational" usually refers to how you organize your people.

Then what, in your opinion, is a better adjective for "technical debt" that best conveys my point?

>How do you organize it into layers? How do you organize it into files?

Namespacing, files, and "layers" are aesthetic forms of organization that do not produce actual barriers in organization. They are for people to read and do not produce actual structured barriers.

  Namespace MathLayer1 {
      function mathlayer1combinator()
  }

  Namespace OtherLayer2 {
      function otherlayer2combinator()
  }
You will note because both functions are combinators, they can always be moved/called interchangeably into either namespace/file/layer provided that you handle circular dependencies (easily done in C++ by declaring everything in a header file first and making sure you don't have any sort of circular recursion in the definitions).

Thus "organizational" mistakes in namespacing/layers are actually trivial because none of the combinators in the namespace are tied to context. If you find you can't move a function out of a namespace it is always because of the fact that that function is not a combinator and is likely relying on some other state declared within the namespace/class. It is not the namespace itself that is causing this issue.

Another way to think about a namespace or layer is that all it does is put a prefix on your combinator for readability purposes.

    Layer3.combinatorG as opposed to just combinatorG
A third perspective is to view the combinator as a concept that transcends namespacing or layers. A combinator is never limited by scope because it carries the scope with it rather then existing as an entity tied to scope.

Machine organization is a problem though. Machines are a physical limitation placed upon our virtual code and people exploiting this limitation as a feature makes code even less modular. A machine barrier is no different from a namespace or object with one difference: Moving or reusing code in a different machine requires data transfer. There is no reason to impose this limitation on our code unless we have no choice.

Thus the limitations of machines should only be utilized to optimize for performance, not as a feature to organize your code. Inevitably in practice this can cause organizational debt if you placed a function in machine A for optimization reasons and suddenly find that you need to use that function in machine B things will be inconvenient.

Hopefully, if that function is a combinator, moving it for use in machine B will be less of a pain. But keep in mind in the idealistic world of programming the machine barrier doesn't exist. Formally moving your combinator out of Machine B into Machine A is the same as if the Machine was called a Namespace. There is no intrinsic difference. It is the physical limitations of the real world that is making things inconvenient so my statement about combinators still holds logically.

That being said the physical barrier of machines can be abstracted away in a single project directory. There are strategies to handle this (imperfectly), docker or RPCs for example.

It's Good to have awareness of the exact formal and logical consequences of certain actions rather then rely on some fuzzy intuition of design. Clarity in the fundamentals of what's going on is key to developing a logical rule set so that optimal structure can be calculated rather then sub-optimally designed from intuition.

Post reply on HN