Hacker News new | past | comments | ask | show | jobs | submit login

>By far the part we're worst at is #1, isolation. If we could truly and efficiently isolate one bit of code from another, the other goals would mostly fall into place. But we simply do not know how.

This might sound a bit mean but the author hear lacks clarity in thinking and therefore really doesn't know what he's talking about.

We 100% know how to isolate code. In fact the primitive that guarantees this isolation is part of the name of this site: combinator. Isolated logic is literally the definition of a combinator.

If every line of your code was written as a combinator then all your code would be reconfigurable and modular to the maximum amount theoretically possible.

The hard lines drawn with physical hardware are largely irrelevant and only obscure the main point. It's not relevant whether your code is isolated by a combinator or hardware acting like a combinator. There is no theoretical difference between using a virtual folder to group things or using a computer to group things.

The main issue with 'isolation' then comes down to IO, because all IO related primitives cannot be part of a combinator by definition and IO is a required part of computation. (by IO I mean input/output and shared mutable memory access)

The article doesn't even mention this. The main problem with modularity is how to deal with it in the face of IO. Hence he's not clear about what he's talking about.

IO breaks modularity & isolation and so does the use of anything that is not a combinator.




I have a question, I am also a total noob on this but you maybe able to teach us (I mean it, not being sarcastic, actually I like your reply a lot).

In Scala, for example, when using functional programming, even IO is represented with an object (take ZIO for example, or Cats effect), does this IO still breaks isolation and modularity?

I have seen code that looks extremely modular, reconfigurable, and isolated (exactly like you mentioned) but even IO (lazy/async) didn't make it bad, but again it is encapsulated and not actually directly dealing with IO and it only materializes at absolute end of the computation.


>In Scala, for example, when using functional programming, even IO is represented with an object (take ZIO for example, or Cats effect), does this IO still breaks isolation and modularity?

Yeah. It does. Think about it. if I have a function called:

  getNumberFromMutableMemory :: void -> IO[Int]
The value of the int is reliant on an external dependency. Whatever IO represents, whether it'd be user input or a database is an external dependency and by definition NOT isolated and NOT modular because the IO function is coupled with the external dependency.

Additionally if the IO is mutable memory the value of the function is also dependent on every single other thing that modified that piece of memory.

The IO types form another type of isolation that is more meta. These types serve to segregate your code away from modular code that basically has NO external dependencies namely the above code cannot be directly composed with a function of this type:

  addOne :: int -> int 
This kind of thing is cleverly done by making it impossible to construct anything of type IO[T] without doing a system IO call. You will find that because of this even if all your functions are typed with IO, you can't really compose them together or even access the internal value of IO without using the bind operator to segregate IO from the world of pure calculations.

>I have seen code that looks extremely modular, reconfigurable, and isolated (exactly like you mentioned) but even IO (lazy/async) didn't make it bad, but again it is encapsulated and not actually directly dealing with IO and it only materializes at absolute end of the computation.

Code being bad or good is a matter of opinion. I'm not saying IO makes code bad, it makes it less modular (and IO is also a required part of all apps, so all apps require a degree of broken modularity). Categorically, anything that touches IO or mutable memory is less modular simply because it has an external dependency on IO. Anything with a dependency is not modular by definition.

What you'll find is that there tends to be a type of IO that can maintain purity of your logic and that is if the IO only goes in one direction: outward. If all you're doing is printing statements to the console or writing but never reading from external memory, you don't even need an IO type to segregate your pure logic, everything will work transparently. Let's say I have a function called print that prints a value but also returns the same value given to it. I can use it without an IO type and it won't effect any of my pure logic:

  print :: Int -> Int
  addOnePrintThenAddOne = addOne . print . addOne
It's only when you start reading from memory that code becomes less composable.

One key to modularity is to see how composable your logic is..

For example:

   addTwo = addOne . addOne
The above is literally the definition of modular code. I take two pieces of logic and put them together to form new logic.

Good luck trying to do the above with IO functions that write and read from IO or mutable memory.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: