Live data from Hacker News

Don't Be Afraid of Types

lmika.org

211–220 of 233 posts

Re: Don't Be Afraid of Types

#211

As somebody who is afraid of types (and also, who hates types, because we all hate what we fear), may my point of view serve as balance: you don't need a type system if everything is of the same type. Programming in a type-less style is an exhilarating and liberating experience: assembler : everything is a word C : everything is an array of bytes fortran/APL/matlab/octave : everything is a multi-dimensional array of…

ruby: everything is an object

Haskell: everything is a function

Re: Don't Be Afraid of Types

#212
post #107

Earlier quoted context omitted.

This can't be the explanation for everything, but I do know that once upon a time just the sheer source-code size of the types was annoying. You have to create a constructor, maybe a destructor, and there's all this syntax, and you have to label all the fields as private or public or protected and worry about how it fits into the inheritance hierarchy... and that all still applies in some languages. Even the dynamic…

> But in many more modern languages, a "new type" is something the equivalent of You don't even need a modern language for that kind of thing, plenty of languages from a half century or so ago also let you do that. From Ada (40+ years old): type PrimaryColor is (Red, Green, Blue); Or if you're content with a mere alias, C (50+ years old) for your first example: typedef char* MyNewType;

It is true that modern is strictly speaking not the criterion. But what I was referring to is that the languages that were popular in, say, the 90s, generally did have that degree of ceremony.

C has its own problem. First, typedef isn't a new type, just an alias. But C's problem isn't the ceremony so much as the global namespace. Declaring a new type was nominally easy, but it had to have a crappy name, and you paid for that crappy name on every single use, with every function built for it, and so forth. You couldn't afford to just declare a new "IP" type because who knows what that would conflict with. A new type spent a valuable resource in C, a namespace entry. Fortunately modern languages make namespaces cheap too.

Re: Don't Be Afraid of Types

#213
post #149

Earlier quoted context omitted.

Because of the cost. Performance (if you optimizer is any good) will be at most a few thousand CPU cycles and so only rarely worth worrying about, though even this can add up if everything is an interface. The larger cost is maintenance. Every time you want to change/add/remove something you now need to touch not only every place that uses the thing in question, but also all the tests. That is by mocking your are tig…

Let me refine: > mocks/fakes do not count Why not?

You apparently have never been in the situation of making a trivial change to production code and then have thousands of tests fail because of overuse of mocks.

Re: Don't Be Afraid of Types

#214

Earlier quoted context omitted.

Not trying to be funny, but "Ravioli code" might be closer: https://stackoverflow.com/questions/2052017/ravioli-code-why... https://en.wikipedia.org/wiki/Spaghetti_code#Ravioli_code A related principle that I don't think is talked about enough is "locality": I'd rather have all the code about one feature in one file or close together, rather than it strewn across files where it's harder to read and understand as a wh…

> make navigation through the code for maintenance purposes more difficult. I have this issue with: Java, Kotlin, Erlang, and Elixir, but especially Java and Kotlin.

I've seen the opposite with Python where there's so much junk in a single file it's also impossible to navigate.

Re: Don't Be Afraid of Types

#215

Earlier quoted context omitted.

> make navigation through the code for maintenance purposes more difficult. I have this issue with: Java, Kotlin, Erlang, and Elixir, but especially Java and Kotlin.

I've seen the opposite with Python where there's so much junk in a single file it's also impossible to navigate.

Yeah, I do not use Python, nor do I have to read much Python code, but it may be difficult to follow, too (OOP in general), just like PHP with lots of classes but I do not mind 5000 LOC PHP code that is fully OOP at least, I probably would if they were in their separate files.

Re: Don't Be Afraid of Types

#216
post #127

Earlier quoted context omitted.

You contradict yourself here. Interfaces are useful for the same reason having private data is a good idea: separating the contract from the implementation. Interfaces are a description of what the consumer of the interface needs, not a description of some commonality between implementations. Interfaces can be useful even if you have no implementations.

Do you want to pay me several years income to write a book on this subject, including the cost of editors, publishers, and other things I'm not even aware of (I've never written a book). This is a very complex subject but I stand by it as a general rule even though there are some exceptions. In general if there is only one implementation just use it.

Not several years' income, but I might pay you up to $50 for it if it's good.

> In general if there is only one implementation just use it.

This is good advice for people who don't understand what interfaces are for -- which seems to be most people. If you make the mistake of believing that interfaces are for describing the commonalities between similar implementations, then demanding an IFoo for every Foo is indeed a waste. Generally if I see literally the same word twice, but one has an "I" in front of it, it's a flag that the interface might not need to exist. I think this is the advice you're giving here. But you've missed the other important point: interfaces are not "about" their implementations, they're "about" their consumers. The point of an interface is for the consumer to define exactly what it needs to do its job. That's why interfaces can be useful even if there are 0 implementations: it's still a clear definition of what some service or function requires. Assuming that service isn't private/internal, it's a hook that a consumer of your module can implement if they choose.

I'd say the frequency of the following patterns is correlated with different levels of code quality -- sometimes causally, sometimes spuriously.

    1. StringWarbler : IStringWarbler  (worst)
    2. StringWarbler                   (fine)
    3. StringWarbler : IWarbler        (maybe worse or better, but a bit smelly)
    4. StringWarbler : ITextProcessor  (good)
Yes, #2 is fine if all you need to do is warble strings and there's really only one thing that means. That's common and it's fine. #3 is suspect because it looks like it was abstracted along the wrong line. It looks like there were a lot of different "ThingWarblers" and someone tried to define what was similar about them, without really focusing on the consumers of these services. Whereas with #4, it looks like someone identified a need to process text in various ways, and one way to do that is to warble them. It sounds like the interface was created to describe a need, not an implementation. When you do it this way, you start to see classes that implement two or three interfaces as well.

When I see #2 everywhere, I don't think "these classes need to be implementing more interfaces!". Instead I think "the services relying on these classes are probably not doing a good job specifying exactly what they need to do their job, and are therefore brittle against changes in how those needs are fulfilled." Whether that's a problem depends on the exact situation. I feel like your advice is "prefer #2 over #3", which I generally agree with. But the better advice is "ask for what you need to do your job, in the most general form you can", which #2 (in excess) violates.

Re: Don't Be Afraid of Types

#217
post #197
post #55

Earlier quoted context omitted.

"Messages that originate outside of the program" is distributed computing, regardless of it is on the same computing node, or across the network. Multiple processes are involved, giving meaning to a byte stream. I am quite sure Interface Builder has enough Objective-C type definitions, as does Portable Distributed Objects. Additionally, those mapping definitions on the UI are nothing else than a type system powering…

> Multiple processes are involved, giving meaning to a byte stream. Maybe in the case of Erlang, but that's a fairly unique case. Smalltalk[1], and by extension Objective-C, do not pass messages across processes. Languages don't normally concern themselves with that kind of thing at all. You can build distributed computing on top of languages (pretty much any language), but that's not a trait of the language and way…

1987 "The design and implementation of distributed Smalltalk"

https://dl.acm.org/doi/10.1145/38765.38836

https://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/html/...

> do not pass messages across processes

Except "The Smalltalk-80 system provides support for multiple independent processes with three classes named Process, ProcessorScheduler, and Semaphore. A Process represents a sequence of actions that can be carried out independently of the actions represented by other Processes."

"Ch. 15 Multiple Independent Processes" 1983 "Smalltalk-80 The Language and its Implementation"

https://rmod-files.lille.inria.fr/FreeBooks/BlueBook/Blueboo...

Re: Don't Be Afraid of Types

#218

Earlier quoted context omitted.

I think you misunderstood the article and my comment: this is about introducing new types, not about typing or not typing your function arguments.

But new types are wonderful! They can be used to prevent you from adding speed and and fuel left. Yes, both speed and fuel left are floats, but adding them will only produce nonsense.

Which is an altogether different discussion: the OP focused on compound types, and not on type "aliases" and their benefits.

Most languages and their typing systems would still allow adding speed and fuel-left if you only aliases them, so you are likely going to need to sacrifice ergonomics by wrapping them in a compound type instead.

Re: Don't Be Afraid of Types

#219
post #207

Earlier quoted context omitted.

Holub [1] helped clarify this for me. Functional programming can express patterns just as well as OOP. Implementations--idioms--of a pattern can appear different but still retain its design purpose. Previously, I thought FP was a way to happy-path incidental habits to avoid studying every pattern. But if patterns are discovered, arise out of independently invented idioms, then the best I could do is reinvent what eve…

> Functional programming can express patterns just as well as OOP. No! Patterns are just crutches for missing language features or "design patterns are bug reports against your programming language." GoF patterns are concepts useful in OOP, but the recurring patterns and architectures you see in other paradigms are totally different. And they don't apply to Lisp: https://www.norvig.com/design-patterns/ Most don't eve…

> missing language features

Yes, Holub mentions C folks had to make do with what they had--but that those implementations, despite differences, would point to one or another common pattern (or done enough times similarly to be a pattern).

> bug reports against your programming language

Yes and no. I daresay no programming language can expect to be a universal one and still have the same ergonomics as a purposeful one or even a DSL.

At the same time, with so many amazing languages out now, new language authors may see adopters clamor for features seen in those contemporary ones.

> Macros... Lisp... Clojure

Yes, definitely have tooling for boilerplate. But that also doesn't mean the pattern could have been implemented differently as yet another idiom, or written in a way that isn't idiomatic according to one group or another.

Thank-you for the references.

We've only been speaking in terms of single languages, though. Have you combined different programs together before and thought, "Hmm, this is kind of like a pattern for..."?

That would speak to the universality of patterns.

Re: Don't Be Afraid of Types

#220

Earlier quoted context omitted.

Inheritance is one way to achieve polymorphism. it is not mandatory for OOP. Unfortunately YES, because of "Java Entreprise" pushed by consultancies 15 years ago, a lot of developers insist on encapsulating everything, even when it's redundant. Fortunately, Java is a better language today.

> a lot of developers insist on encapsulating everything, even when it's redundant. Can you give an example? 15 years ago was 2010 and Java 8 was already released. > Fortunately, Java is a better language today. In what ways? To me, it has barely changed since JDK 8 when it got lambdas. To be clear, the JVM is leaps and bounds better each long term supported release.

Sealed interfaces and records allow you to effectively build sum types. Switch expressions with type based pattern matching really streamline certain patterns. Type inferred vars are massively welcome. Streams and lambdas are pretty old by now, but they are much more widely embraced in e.g. frameworks and std libs which is nice.

Independently none of these are game changing, but together they provide much improved ergonomics and can reduce boilerplate. Now if only we had null safe types <3.

Post reply on HN