Live data from Hacker News

The Wrong Kind of Paranoia

prog21.dadgum.com

41–50 of 54 posts

Re: The Wrong Kind of Paranoia

#41
post #36

I usually love James Hague's posts, but this one has his head buried deep in the sand. And he's enough of a veteran to remember early Lisp and Smalltalk systems, where excessive openness in all aspects looked awesome for prototyping/hacking or academic toy programs (or what is considered toy-size in the last 20 years anyway), but was a disaster even for the mid-1990's definition of programming in the large. Attempts…

> we have tried the elegance of extremely simple and open language/runtime architectures, and that was always an abject failure.

Just because it was tried in the past, doesn't mean it wasn't the right direction.

> Attempts to discipline these languages came too late,

So, as an industry, we were learning and now we know some basic principles. Discipline does a great deal for Erlang. Ironically the game sockets he describes are basically how Erlang functions at scale. To say the practice is always an abject failure is burying your head in the sand.

Re: The Wrong Kind of Paranoia

#42

This post resonates with my experiences of the last 6 months. I'm a very good web developer, and have been working for a while now on a couple iOS projects. Coming from javascript which everyone seems to regard as dangerous and overall terrible, I expected that after some time I would get used to and appreciate working in Objective-C and Swift. Nothing could be further from the truth. While you can shoot yourself in…

This old canard. I am a professional Objective-C developer and have been for several years. Type safety is not an absurd length. Eliminating an entire class of errors from your program by having the compiler infer and enforce types is not ridiculous. Using a type-safe language is a very good idea. We are human. We have stupid unchecked nil object errors come up in our code bases all the time. Swift will ensure that d…

Is it really worth that much though? I'm a Ruby developer and I've only very rarely actually wanted a type system. I've often found that I could work around its lack by implementing class checking and raising an error whenever the wrong type gets passed. It gave me everything I wanted without having to give up dynamicism.

I think types probably work well in situations where you don't know what kind of code you're going to have to deal with in the future. It depends on the type of organization you're in, not the type of problem you're trying to solve.

I think any respectable programmer should try to avoid having to have people hook into his code at any level other than the level he defines. To interface at the level of data, not client code. If you are needing typing to solve your own inadequacies as a programmer, you should become a better programmer rather than expect your language to do that for you.

With a dynamic language, you can get all you could have wanted from a type system without having to infect your whole codebase with it. Most of the time, you just don't need it.

Re: The Wrong Kind of Paranoia

#43
Most of the commenters here didn't seem to read James' whole article. He's not saying that you should throw away isolation but that isolation should be taken care of from your architecture, not your programming language. Of all the places that should be up on a microservices architecture and see the sanity of it, you would think that would be here on HN, no?

Re: The Wrong Kind of Paranoia

#44

Earlier quoted context omitted.

This old canard. I am a professional Objective-C developer and have been for several years. Type safety is not an absurd length. Eliminating an entire class of errors from your program by having the compiler infer and enforce types is not ridiculous. Using a type-safe language is a very good idea. We are human. We have stupid unchecked nil object errors come up in our code bases all the time. Swift will ensure that d…

Is it really worth that much though? I'm a Ruby developer and I've only very rarely actually wanted a type system. I've often found that I could work around its lack by implementing class checking and raising an error whenever the wrong type gets passed. It gave me everything I wanted without having to give up dynamicism. I think types probably work well in situations where you don't know what kind of code you're goi…

I agree that some type systems are infuriating. They demand highly verbose code, and offer almost no protection in exchange for your effort.

However, not all type systems are equal. Haskell (for example) almost never requires explicit type annotations. It has a type inference system that is sometimes frighteningly good. You can express a huge amount of logic through the type system and enforce very non-trivial constraints.

I've been writing Ruby for 7 years, and I've loved every moment of it. It's a wonderful language. That said, I usually have to spend quite a bit of time getting my code to work correctly. In Haskell, by the time I get to the point where the type checker approves of my code, it usually works as I intended the very first time I run it. It's a wonderful feeling.

Re: The Wrong Kind of Paranoia

#45

In my view the purpose of const/protected/internal/static etc is not so much to prevent mistakes by myself or others, but to embed in my code a 'living documentation', enforced by the compiler. If I mark a method as internal, that means I intend it for reuse within the library but don't expect it to be used by any external caller, which means another developer can come along and make changes to it without needing to…

Strongly agreed.

> const

I don't have to worry as much about iterator invalidation with containers, because I've documented that I'm not going to change it. Bonus points: I know the documentation isn't lying like it usually is. (Caveat: Someone const_casted it away, and my trust is forever crushed ;_;)

I don't have to scan for side effects to figure out if this const variable is an alias for "the initial size of the container" or "the number of elements remaining to process, which just happens to start as the initial size of the container".

It's clearer if the vector normalization function is "mutate in place" or "return a copy". I've seen enough code that does both that I can't necessarily rely on eyeballing the return type.

I can slap it onto existing, likely side-effecting code, to help make sure I don't miss anything when refactoring to an immutable style, because e.g. I'm preparing for multithreading.

That it helps catch accidental mutation is merely a happy side benefit.

> protected/internal

These are worth it just to unclutter my intellisense, which is how I read most of my documentation on some projects. Because it contains all the documentation, or because the API is straightforward enough that it contains all the necessary documentation.

> static

I've had this both help and harm. I'm typically using C++'s anonymous namespaces instead. I have collisions frequently - localized log methods, error tables, local module allocators, etc.

This is less about "I want to prohibit external calls" and more about "This is throwaway local code that I don't want to painstakingly disambiguate from every other possible chunk of throwaway local code." - I see frequent enough collisions with local log methods (which annotate before calling global log functions), error tables, shorthand aliases to specific allocators, etc.

And now to quote from the article instead:

>> What all of these fine-grained controls have done is to put the focus on software engineering in the small.

I'll certainly agree that fetishizing these micro tools to the detriment of larger system issues is bad. That's more along the lines of "insufficiently wide paranoia" than "the wrong kind" though.

>> There's an architecture used in video games for a long time now where rendering and other engine-level functions are decoupled from the game logic, and the two communicate via a local socket.

You can overcouple via RPC over a socket just fine. Like module level protections, it's a mere speedbump against making a bad decision. There's no panacea.

Re: The Wrong Kind of Paranoia

#46

Earlier quoted context omitted.

This old canard. I am a professional Objective-C developer and have been for several years. Type safety is not an absurd length. Eliminating an entire class of errors from your program by having the compiler infer and enforce types is not ridiculous. Using a type-safe language is a very good idea. We are human. We have stupid unchecked nil object errors come up in our code bases all the time. Swift will ensure that d…

Is it really worth that much though? I'm a Ruby developer and I've only very rarely actually wanted a type system. I've often found that I could work around its lack by implementing class checking and raising an error whenever the wrong type gets passed. It gave me everything I wanted without having to give up dynamicism. I think types probably work well in situations where you don't know what kind of code you're goi…

I don't know much about Ruby, I will admit.

But dynamic typing is terrible for large code bases. The only place I allow it is at the very edges of our system, where we are transforming the proper types of the internal system into POD-objects to be serialized and returned to the JS/HTML front-end. I'm not willing to give up the compile-time safety of breaking the build if someone inadvertently assigns a string to a number, or an object to a primitive in code that they check in, in place of unit testing that, odds are, will be incomplete or otherwise broken and won't catch the problems that static typing catches.

Re: The Wrong Kind of Paranoia

#47

Earlier quoted context omitted.

This old canard. I am a professional Objective-C developer and have been for several years. Type safety is not an absurd length. Eliminating an entire class of errors from your program by having the compiler infer and enforce types is not ridiculous. Using a type-safe language is a very good idea. We are human. We have stupid unchecked nil object errors come up in our code bases all the time. Swift will ensure that d…

Is it really worth that much though? I'm a Ruby developer and I've only very rarely actually wanted a type system. I've often found that I could work around its lack by implementing class checking and raising an error whenever the wrong type gets passed. It gave me everything I wanted without having to give up dynamicism. I think types probably work well in situations where you don't know what kind of code you're goi…

> If you are needing typing to solve your own inadequacies as a programmer, you should become a better programmer rather than expect your language to do that for you.

That is one of the most ignorant things I have ever read.

Perhaps if you are needing dynamicism to solve your own inadequacies as a programmer, you should become a better programmer rather than rely on your language like a crutch.

Perhaps if you were an adequate programmer, you wouldn't have such a hard time understanding types and getting the compiler to accept your program.

I cannot imagine a worse programmer than one who willfully rejects a tool which improves a codebase to great extent. Typed languages are strictly more powerful, and you cannot get the same code guarantees from a dynamic language.

Re: The Wrong Kind of Paranoia

#48
post #18
post #12

To be clear, static typing is the zenith of of compiler optimizations and in many ways the programming "ideal," however in the context of loosely typed languages (and their OOP abstractions) and I think the author is missing a significant use-case. Separation of responsibility/decoupling on the service level are good principles to begin with, but in an OOP paradigm where you are defining/exposing classes and methods,…

A nice standard reply. But how do you verify that your usage has the impact you think it does? When I started estimating the complexity of my code - just using rule of thumb and line counts - I found that most uses of classes were unjustified. The "right size" of a class was quite large, especially so in the top level of an application.

Well, I can't speak necessarily for the way you write or view code, but for my process I've found its impact intrinsic and clearly definable.

First I should note that I follow a few basic principles/sets of principles when programming loosely-typed imperative languages.

1) Single-responsibility for classes and methods, with each method coming in around 2) I build an interface before building a class--the interface defines what methods will be available to the application/world context (thinking as per an API interface), as well as what type(s) should be received and what type(s) should be returned. By type hinting/checking at this stage and ensuring conformance to an interface, you can swap out implementations easily later, as well as have a general map or "spec" before you really start hammering the nails--this helps in staying organize and weeding out bad architecture decisions early.

3) I keep these public methods simple, so that I can clearly detect failure points and debug based on input/return types for the 'service' as a whole, and then I use protected methods internally similarly to data pipes in functional programming; each method is clearly named, has a specific transformation it applies, and acts on a series of (n) objects by mapping transformations vs iterative loops which precludes un-terminated conditionals and other type-juggling weirdness.

This all results in software that's very concise (IMHO), runs well, and is quite simple to test. I can inherit any class from a testclass, in order to test the protected data pipes with any sample streams. I can verify the I/O types for all interfaced methods of the class (via functional tests), and tie it all together neatly in knowing that I can pull up any file and clearly differentiate between what formats/returns/transports data, and what mutates it and/or the "state."

The process is by no means perfect and I continue to learn in my pursuits as do we all, however this structure has worked well for me consistently on the types of large projects where others have failed, and in that context I feel it's worth expounding.

Re: The Wrong Kind of Paranoia

#49
I find the assertion that compiler enforced discipline is paranoia, too hippy.

What it does is free you from a big overhead, which is keeping track of the access levels for all of your instance variables. Moreover, documenting enforcements is a very shaky way to go about it. It requires military discipline. I've seen time and time again, documentation which didn't get changed with the code.

Re: The Wrong Kind of Paranoia

#50
post #26

Earlier quoted context omitted.

Python being Python, you can prevent non-malicious problems of the sort you mention (const and private functions / variables). Const via overwriting setattr / etc, private functions via stack inspection (among other things. There's a sliding scale of thoroughness versus clean code here.) Though it won't protect you from malicious intent (though this is still the case with C / etc), and it's caught at runtime as oppos…

> Though it won't protect you from malicious intent It often does feel like junior developers are malicious. While I commend their ingenuity in solving the problem, code review can be a real schlep.

Hanlon's razor
Post reply on HN