Live data from Hacker News

Lisp and Haskell (2015)

markkarpov.com

111–120 of 173 posts

Re: Lisp and Haskell (2015)

#111

Earlier quoted context omitted.

There is a substantive difference between "couldn't get any users because of an error" (Nothing) and "succeeded but found no users" (Just []). But, if that really is the reason for Maybe [a] over [a], I'd still prefer Either SomeUsefulErrorType [a].

`Either` would be much better, no?

No, because `Either` is a `Foldable`, too.

I know, not what you meant. But a custom sum type would not only allow you to return multiple different failure cases, you also wouldn't give it a `Foldable` instance with surprising semantics.

Plain old data types are seriously underrated these days.

Re: Lisp and Haskell (2015)

#112
post #73

Earlier quoted context omitted.

I'm not sure what "Haskell developers" are saying, but this Haskell developer says "Haskell provides many tools that can be used to solve programming woes. One such tool is its strong and flexible types system (there are several other tools, such as immutability, functional style)."

And Python provides readable syntax . Your example has an easy-to-spot bug, just from reading the code and knowing Python's semantics. Haskell's "programmable semicolon" ends up being a problem here because, thanks to typeclasses, the programmer usually does not know which semicolon they are programming under! "Python provides many tools that can be used to solve programming woes. One such tool is its strong and read…

I don't think that illustrates that promotion of the type system as a useful tool is an "empty platitude". A well constructed syntax is certainly a nice feature of a programming language. One might say a language like brainfuck does not have a syntax which makes the code easily readable, and therefore is the worse for it.

Similarly, one might say that a strong type system is a useful tool. That doesn't make it a panacea (and I don't think that this was the claim), but that doesn't mean one can't appreciate it as a good tool.

Re: Lisp and Haskell (2015)

#113
post #5

> if your code compiles, it probably works I'm always a little frustrated whenever I see this aphorism perpetuated since I think it gives the impression that a static type system is doing more than it is actually doing. In type systems that are used outside of academia, the main thing your static types are doing is checking whether the shapes of your data and functions all line up. With some small exceptions, that's…

Not just shape of data, but the values that are allowed by data.

I agree that it is an oversold fallacy and types get really clunky when pushed too far. But any worthy type system prevents lot of errors. This is not generally acclaimed by empirical studies claiming only 5% improvement because they are caught at compile time.

Other benefits are enough to use a sufficiently good static type system, by the way, even for people that think type systems aren't better than TDD.

    * Better autocomplete
    * Better error highlighting - no need to run code or tests, IDE can highlight instantly.
    * No typos in variable names (can be achieved in some but not all dynamic languages)
    * Performance of compiled code

Re: Lisp and Haskell (2015)

#114

Earlier quoted context omitted.

Why does getUsers return Maybe [a] in the first place? It should be [a]. You can also implement your own version of length that works only with iterables of your choice. The language allows leveraging type constraints to achieve this, the fact that your team didn't use it doesn't indicate an inherent flaw in the type system you've been provided with. And if you want to go an extra mile, use liquid-base instead of bas…

There is a substantive difference between "couldn't get any users because of an error" (Nothing) and "succeeded but found no users" (Just []). But, if that really is the reason for Maybe [a] over [a], I'd still prefer Either SomeUsefulErrorType [a].

Right, so we essentially say that the provided example doesn't refute the "it works if it compiles" slogan. It just works according to the provided specification expressed in the types, and it so happened that the specification was not specific enough.

My specification doesn't make a distinction between absent users and possible error responses because I assume that errors will be processed elsewhere and the getUsers function is applied as part of the "safe core" interface that is not concerned about error handling. If the specification was more precise (even more precise than in your suggestion, for instance, getUsers :: SafeIterable a => Foo -> a and length :: SafeIterable a => a -> Integer ), it would work differently.

The issue in the example has to do with the clarity of the intent encoded in the type signature, not the inherent issue of the type system that cannot guarantee safe runtime of the example. That's why I mentioned type constraints for specific iterables as a possible solution to the encoding problem.

A better example of the compiled-but-not-working case would be something that currently cannot be expressed well by the type system, something like async exceptions or access to closed resources (will be covered by linear types soon).

Re: Lisp and Haskell (2015)

#115

Earlier quoted context omitted.

> I guess you probably don't write tests or documentation either? Why would any experienced programmer make such a blanket statement? As always, it depends. Sometimes there is value in documentation (or whatever unchecked types in fancy languages), especially for stuff that is read a lot more, by other people, or yourself in the future. For a lot of stuff there is no point in writing documentation. For example, if th…

The fact that code and types don't diverge can make types useful as documentation, albeit limited documentation. Ideally the compiler figures out the types so you don't have to. Haskellers often find this useful when they refactor a month later.

> Ideally the compiler figures out the types so you don't have to.

Doesn't it cause unintended API breaks because type changed from, say, a single type to union type, without changing any annotations?

Re: Lisp and Haskell (2015)

#116
post #39

In Clojure it's common to develop your application by building it inside a running system Every REPL form sent recompiles the running program, I don't want that fast loop to be force lagged by the program checking types I'm more than happy for that to happen at edit time, independent of compile time outside of my application process So I heavily encourage using clj-kondo to check for mistakes doing primitive type che…

I hear this "code as system is running" a lot. Doesn't it complicate our reasoning about the program as compared to simple text files? I haven't heard the other side of this claim.

Re: Lisp and Haskell (2015)

#117
post #90

Every once in a while, there's a post on front page HN about Haskell and/or Lisp. Sometimes these posts get a lot of traction, but what confuses me is despite the apparent popularity of these languages among developers, still they are seldom used in serious software. I know there are exceptions (esp. with regards to Lisp), but still these languages never come close to other languages such as Java, JS, C, or even Scal…

Just wait for symbolic computers to be rediscovered and the industry will probably adopt lisp and prolog's successors.

One can dream.

Re: Lisp and Haskell (2015)

#118
post #5

> if your code compiles, it probably works I'm always a little frustrated whenever I see this aphorism perpetuated since I think it gives the impression that a static type system is doing more than it is actually doing. In type systems that are used outside of academia, the main thing your static types are doing is checking whether the shapes of your data and functions all line up. With some small exceptions, that's…

> static types are ... checking whether the shapes of your data and functions all line up

They definitely do more than that in Haskell. You can define distinct types with the same "shape", and the `newtype` keyword specifically creates a new type that has the same "shape" as an existing one, but is nominally distinct.

This nominal typing, as it's called, allow you to express intent in types. Type checking then doesn't just check whether "shapes" line up, but also whether your intentions line up. If you express contradictory intentions, you probably made a mistake.

Practically speaking, if the only types ever used in a Haskell program are `Int`, `Float`, `Char` and nested lists of those, all the type checker can ever do is tell me when I miscount square brackets or forget a `fromIntegral`. Those programs never run on the first attempt.

Re: Lisp and Haskell (2015)

#119
post #64
post #61

Earlier quoted context omitted.

Agreed. I have a strong background in 'bad' programming languages like Go, Python, C. Half a year ago I had a short stint at a Haskell shop and was thoroughly disillusioned. That particular 'writing against the compiler' approach lended itself to write-once, over complex type juggling code. Because why make it readable - if it compiles, it works, and if it works, there won't be any need to read it, right? Well, excep…

The Foldable instance for Maybe is one of the biggest warts in Haskell. It's not limited to Haskell, by the way. def getUsers(f): return (logged_in_users(f), logged_out_users(f)) def userCount(f): return len(getUsers(f))

To make this code clearer:

    def get_users(f) -> Tuple[List, List]:
       return (logged_in_users(f), logged_out_users(f))

    def user_count(f) -> int:
       return len(get_users())
Now it's clear this is just wrong code. To fix it:

    def get_users(f) -> List:
       return logged_in_users(f) + logged_out_users(f)

    def user_count(f) -> int:
       return len(get_users())

Re: Lisp and Haskell (2015)

#120
post #31

Earlier quoted context omitted.

I find these generally worth it because you can surface those aspects up to the function signature. It would be a lot nicer to have a function signature that shows the limits than it is to have a failure and have to open the docs for the library to see what the limits are. If the code is sound, it should have the same complexity anyways. I.e. putting a non-empty requirement in the function signature is just as comple…

I used to think Javascript was a great prototyping language. In fact I specifically practiced prototyping with it -- my github is littered with repo's for each of my prototypes -- I call them the Laundromat Demos. I would use the 70-80 minutes I had at the laundromat to write a prototype for a game in JS. However I've tried something similar with Haskell. I think I prefer Haskell for prototyping these days. It turns…

+1, I'm impressed how far these prototypes can get you, especially if there's an extra layer of correctness on top of the existing Haskell type signatures. For instance, LiquidHaskell [1] can naturally shift you towards implementing most of the necessary validation and parsing logic as part of your work on precise type signatures.

[1] https://www.youtube.com/watch?v=zCJV0xNY06o

Post reply on HN