Live data from Hacker News

Haskell is our first choice for building production software systems

foxhound.systems

111–120 of 297 posts

Re: Haskell is our first choice for building production software systems

#111

Earlier quoted context omitted.

> You have to repeat the type information, a lot. Nope you don't, that's what typedefs are for. They're underrated for sure though. People don't use them nearly as much as they should. They're incredibly valuable for avoiding precisely this problem.

That doesn't stop you having to type e.g. String foo = "bar"; String baz = foo; The `String`s can be completely avoided in languages with type inference because it's obvious that a string literal is a string.

And it's my experience that that is only a benefit to the person who wrote the code, and only for a short time.

Generally, I prefer being able to read a line of code and understanding exactly what it does. If I need an IDE and have to repeatedly try to find the definition of something then, in my opinion, that's wasting my time.

C++'s 'auto' is really useful but it's over-used IMO. I think that there's a belief that if you're not using 'auto' everywhere then you're not writing 'modern' C++. Just becuase your code compiles doesn't necessarily mean it's correct.

Re: Haskell is our first choice for building production software systems

#112
post #82

Earlier quoted context omitted.

Why couldn't it be a function that queries a database, like answering "on invoice x, does line y exist"? Genuinely curious, not that familiar with Haskell, just thought you could use something like parameter binding or similar to construct functions like that.

if it has side-effects, it has to be something like Int -> Int -> IO Bool otherwise the type checker won't let you perform any side-effecting operations (btw Haskell's `IO Bool` would be spelled `IO ` in C++/Java syntax)

And there's no way to get rid of the IO then, presumably (I mean otherwise I could have just used that as a wrapper).

edit: So at work, just about every value depends directly or indirectly on stuff that comes from files or from the database. So would they all have to be wrapped by IO?

Re: Haskell is our first choice for building production software systems

#113

What I'd like to understand: When/why does one choose Haskell over other functional languages e.g. F# or OCaml? It looks to me like they would satisfy the same points that the article makes. Edit: just did a quick comparison of the last 2 SO developer surveys, and it looks like Haskell "replaced" F# in their popularity ranking last year.

all them are equally competent languages. F# with corporative support and a giant ecosysten, ocaml being very fast and portable and haskell being very... special and pure.

Thanks for your reply!

I see the corporate aspect of F#, but can you elaborate on what you mean by "special" about Haskell?

Re: Haskell is our first choice for building production software systems

#114

Earlier quoted context omitted.

That doesn't stop you having to type e.g. String foo = "bar"; String baz = foo; The `String`s can be completely avoided in languages with type inference because it's obvious that a string literal is a string.

I thought the complaint was about the logic duplication, not the extra keystrokes. If you want type inference you already have auto. If you want to minimize your keystrokes, you're using the wrong language to begin with, whether there's type inference or not. C++ is designed for writing software robustly, not quickly. (<-- This is not a trivial or obvious statement btw. It took me several years to grasp this. And I v…

> If you want type inference you already have auto

or auto&&. Did you really intend to make a copy?

Re: Haskell is our first choice for building production software systems

#115
post #103

Earlier quoted context omitted.

> To clarify the sibling, a signature of `Int -> Int -> Bool` can't do any IO (so no connecting to databases, reading/writing files, network requests), so it does tell you a lot about a function. It doesn't help if it's `Int -> Int -> IO Bool`, for example. Well, it does do IO, but other than that, who knows. Perhaps it reformats the disc while CPU is idle :) My main point though is that the article does a very poor…

It's the fact that it is `Int -> Int -> Bool` that ensures you that the function doesn't do any random thing (like doing something on the database). Combining non-IO capable functions is quite helpful in reasoning about programs. I.e if the function has `IO` in the result type then exactly - "who knows". But with the fact that it doesn't, you do know that it doesn't do anything other than return the bool.

I mean. Really. That's what people hang up on?

The point for that particular gripe was this: "this allows a programmer reading Haskell code to look only at type signatures when getting a sense of what a certain piece of code does."

Yes, IO tells that ... the function does some side effects. And that's it. If your type signature is `Int -> Int -> IO Bool`, it's just as useless as `Int -> Int -> Bool`, and requires you to read the function to understand what it actually does.

Is it helpful to see at a glance which functions produce side effects and which don't? Yes, it is. Does it automagically "allow a programmer reading Haskell code to look only at type signatures when getting a sense of what a certain piece of code does"? No, no, it doesn't. `... -> IO Bool` may be launching nukes for all the information you glean from its type signature.

Re: Haskell is our first choice for building production software systems

#116
post #60

Earlier quoted context omitted.

Why couldn't it be a function that queries a database, like answering "on invoice x, does line y exist"? Genuinely curious, not that familiar with Haskell, just thought you could use something like parameter binding or similar to construct functions like that.

Haskell functions are pure, which means they can only access/use what is in their parameters, so unless you pass in some extra context (typically using a monad or an effect), you do not have access to the "outside world".

You can bind parameters though in Haskell, no?

I'm used to Boost.Bind and similar, so was thinking a scenario where you bind the database connection parameter and pass the resulting function to something else.

As the sibling pointed out though, I now get that the result would be "tainted" so to speak.

Re: Haskell is our first choice for building production software systems

#117
post #14

Earlier quoted context omitted.

Actually, the reason why I found static typing annoying in the past (and why I felt more productive in Python) were the types are really low level (missing basic things like tuples) and lack of type inference. You have to repeat the type information, a lot. And also you have to declare lot of intermediate data structures. In Python, this became easier and one could focus on the data transformations, thinking about th…

> You have to repeat the type information, a lot. Nope you don't, that's what typedefs are for. They're underrated for sure though. People don't use them nearly as much as they should. They're incredibly valuable for avoiding precisely this problem.

Shifting topics a bit, typedefs don't allow me to write generic code. Templates do, but templates bring in their own problems, in addition to not being expressive in the right ways: I can have an array of T, but I can't specify that T is Numeric?

The fact C++ doesn't have Numeric but instead has int and long and unsigned and long long and float and double all off on their own is another problem: The compiler knows enough about them to have complex promotion rules but doesn't know enough to allow me to refer to all of them under one name in my code.

Re: Haskell is our first choice for building production software systems

#118

Earlier quoted context omitted.

I thought the complaint was about the logic duplication, not the extra keystrokes. If you want type inference you already have auto. If you want to minimize your keystrokes, you're using the wrong language to begin with, whether there's type inference or not. C++ is designed for writing software robustly, not quickly. (<-- This is not a trivial or obvious statement btw. It took me several years to grasp this. And I v…

> If you want type inference you already have auto or auto&&. Did you really intend to make a copy?

You probably did intend it to be a copy if you're binding it to a variable and need it to be non-const (like in the example)!

Re: Haskell is our first choice for building production software systems

#119
post #10

Haskell is nice and all, but I'm not a huge fan of this take. I can't help but think that many of the arguments boil down to something like 'you can write types so that the compiler checks things for you' (not a quote), whilst the author disregarded the Java/C++ compiler as "an annoyance" (a quote). The rest of the article is mostly a comparison between Haskell and PHP/Python/JavaScript, and most laid out benefits bo…

> the author disregarded the Java/C++ compiler as "an annoyance" (a quote).

To be fair, the context of that quote is:

> Many programmers encounter statically typed languages like Java or C++ and find that the compiler feels like an annoyance.

I think this is a fair statement, although it would also be fair to say "many Java and C++ programmers find their compiler errors useful". I'd guess these two camps would remain mostly the same when using Haskell.

You're right that most of the article is roughly comparing a good example of static types (Haskell) against a bad example of dynamic types (PHP).

> I've seen this a million times in Java, and that works fine.

My biggest problem with Java (and the JVM) is the existence of `null`: it completely undermines type signatures. In the above Haskell example we "know" (see caveat below) that a `myInvoice :: CustomerInvoice` is a `CustomerInvoice`, whilst in Java a `CustomerInvoice myInvoice` might be a `CustomerInvoice` or it might be `null`; likewise `myInvoice.billableItems` is a `[String]` in Haskell, whilst in Java it might be a `List` or it might be `null`; in the former case, each element might be a `String` or it might be `null`.

Caveat: Haskell values are lazy by default, so errors may only get triggered when inspecting some deeply nested value; in that sense we might say that a Haskell expression of type `T` might be a `T` or might be an error (known as "bottom"). We certainly need to keep that in mind, but one nice thing about bottom is that it can't affect the behaviour of a pure function (we can't branch on it). In that sense returning a value containing errors, which are later triggered, is practically equivalent to triggering the error up-front (pure expressions have no inherent notion of "time", unlike imperative sequences of instructions). The interesting difference is that we can also use such values without triggering the errors, iff the erroneous part is irrelevant to our result ;)

Having all types nullable by default makes 'proper' null-checking incredibly verbose, not to mention tricky; the alternative is to cross our fingers and hope our assumptions are right. What makes this frustrating is that such checks are exactly the sort of thing that computers can help us with, and type systems are particularly well suited for! Hence the presence of `null` cripples Java's type system in a way which can't be worked around (without essentially layering a separate, null-less type system on top to check for nulls!).

Also note that the presence of null causes every domain model to collapse. Let's say we want to write a conversion method, e.g. from `CustomerInvoice` to `Document`, and we don't want to worry so much about `null`: hence we write in our javadoc that as long as the given CustomerInvoice contains no null values, this method will never return null; let's say we throw a NullPointerException in those invalid cases. Great, our users now have fewer edge-cases to worry about; they don't have to check for null, and they don't have to catch NullPointerException if their input is correct.

Except, once we start implementing our method we find it needs to call some other helper method, e.g. `statusToTable`; if that method returns a null result, we would be unable to construct the `Document` value that we promised. What can we do in that case? We promised we wouldn't return `null`, so maybe we throw a NullPointerException? If we do that, those calling our method might get a NullPointerException even if they gave valid input! We might throw a different exception instead, like AssertionError, but the effect would be the same. Hence we can't guarantee to our callers that we don't return null (or some equivalent that they must deal with, like NullPointerException or AssertionError); that, in turn, means they can't provide such guarantees to their callers, and so on. At any point, we might get a null (or equivalent exception), and the whole house of cards comes crashing down.

Maybe we trust that helper method doesn't return null, but how can we know? Maybe we check its documentation or source code to see whether it might return null; but we find that it calls other methods, so we have to check those, and so on. If we do this, we would also have to pin our requirements to the precise versions of the libraries that we checked. In case you couldn't tell, that process is essentially manual type checking (for a very simple system with two types: 'Null' and 'AnythingElse').

Of course, this is sometimes inherent to the problem, e.g. if a HashMap doesn't contain the entry we need then there's nothing we can do. However, most code doesn't have such constraints (except perhaps out-of-memory), but there's no way to tell that to Java (in mathematical language, Java weakens every statement to admit trivial proofs).

Re: Haskell is our first choice for building production software systems

#120
post #93
post #44

Earlier quoted context omitted.

How about servant, and some popular js framework on top of that? IHP might be putting a lot of effort into the project, but the code generation part... I (personally) don't like that at all. And it's not common practice in haskell.

IHP is very opinionated in the way how it approaches building web applications (Code Gen, Project structure, Naming, ..). But exactly this kind of opinionated design makes it possible to be very productive, compared to doing everything yourselves.

IHP is opinionated in the sense that it does state on the server side and uses (something similar to) turbolinks to make it look fast. But generating additional types for SomeDataType like ViewSomeDataType etc - my gut feeling tells me these should be implemented trough type classes instead. New data types shouldn't be generated for cases like this.

Disclaimer: I've only looked at the docs of IHP, but this was what it looked like it was doing.

Post reply on HN