Thoughts on Haskell
khanlou.com
khanlou.com
For example, the author complains about the obscure operators. I'd agree, some can be obscure, but the beautiful thing about Haskell is that they are not language operators, but rather just functions, that anyone can define themselves in the language! The fact that the power to do that is made available to the programmer, in a safe way, is something very few other languages manage.
The point about map, fmap and liftM (as I understand it, I might be wrong) is not entirely correct, in that they apply certain constraints to their usage. This helps to document the code, and also allows us to use precisely what we need, and no more, which leads to safer programming.
As for whitespace - no one likes to read or maintain code with inconsistent whitespace, which means whitespace is significant, regardless of whether the compiler cares or not. So I find this complaint is worth less than many people think.
Finally, the infix notation doesn't look particularly pretty, but you can also define infix functions, so in practice I'm not sure this matters too much.
> For example, the Haskell function elem returns whether an element exists in a list. Do you call it with the list first or thing to search? There's no way to know.
Well, the type of `elem` is
(Eq a, Foldable t) => a -> t a -> Bool
So, it is - with practice - clear to see that the singular item comes first, and the item in the foldable container comes second. Perhaps this sort of Type-Driven Development, which is more of a practical mindset than language feature, isn't emphasized in the (excellent) books from which the author was learning...but it becomes an invaluable tool in Haskellers' toolboxes while trawling through package documentation later on. Many times, between the name of the function and type of arguments, one can figure out exactly what they need to pass in to achieve the desired output.Yes of course because the parameters are usually designed with currying in mind, so the order is from most general on the left to the most specific on the right. In the case of elem, both orders are reasonable, but often times only one makes sense and then it is easy to remember.
The point of my comment was more that one of the beauties of Haskell is that positional arguments are made so elegant because every positional argument is accompanied by a type. I wasn't making a statement about the order of parameters themselves, but how all parameters are typed.
> In the case of elem, both orders are reasonable, but often times only one makes sense and then it is easy to remember.
In some languages where callbacks taking positional arguments are idiomatic (like Javascript), untyped functions can cause a lot of problems by making it really hard to discern/remember what gets passed in where. In Haskell, there's often very little onus on the developer to remember anything at all; the type system will make sure they got the order right.
Obvious exceptions are non-associative functions of the type
a -> a -> a
Then, one might have to check to see what goes in first and what goes in second.You mean "non-commutative"
x 'elem' xs
As a former Perl guy, my opinion is that when you get people the opportunity to use ASCII to produce illegible code, you're sure to find people to do exactly that. That's one of the things that turned off Haskell (and Scala to a lesser extant).
Also, I think one crucial thing is that the strictness of usage, enforced by the compiler, can help ensure that the code is used in the right way. I would feel a lot less safe doing it with Python for example (what I use day-to-day).
Also, Haskell has unicode source files, so there's even more scope, and it can lead to some funny results: https://hackage.haskell.org/package/acme-lookofdisapproval
92 `div` 10
> I think this is pretty ugly, and it feels like it was glommed on to the language in some kind of compromise. For div specifically, you can use the operator / to achieve the same thing, but this won't work for all other functions.I don't see what's wrong with backticks for infix functions; they highlight a change in fixity that makes it easier to see that `div` is being applied to the surrounding arguments rather than being used an an argument:
a div b
a `div` b
I'm not familiar with other languages that have support for named, infix functions. Are there better syntactic alternatives? a :div: b
would look better, but now that I'm used to the backquotes, I'm not so sure.A "dyadic function" in q/kdb+'s .q namespace has a kind of Currying that accepts the right-hand value as the second argument to the function. (This is abusive in practice since the user should never add to the .q namespace as future conflicts are possible.)
.q.add:{x+y}
5 add 10> you can use the operator / to achieve the same thing, but this won't work for all other functions
Yes, it will work for all other (binary) functions. If there isn't an operator at the Preface, just declare one.
Anyway, I'll have to agree with people further down. Using the grave accent as a stand-alone symbol in a language is a big mistake.
It's baffling to me that people find the first two weeks of language experience so interesting. Every language, even incredibly productive ones, have idiosyncrasies that get you in the beginning.
Lets instead talk about how A compares to B after 6-9 months with similar sized teams. Lets talk about on-boarding new hires once the organization has built a skilled team in that environment.
It reminds me a bit of marketing claims vs. reality for a product.
This is what gets me: Haskell articles tend not to stop at presenting this is as a neat and elegant definition of a sort (which it is!) but emphasize that you're a just being picky if you're not willing to brush aside memory usage and reasoning about such in production code.
I suspect the next Holy Wars in programmer practice will revolve around "how much can we get away with not writing out?"
One quibble though -- he complains about the ugliness of infix functions, but misses that you can define your own infix operators, with their own precedence rules! Just put parentheses around the symbol(s) when defining the function, and it becomes infix by default.
Haskell and its whitespace sensitivity are older than Python.
For instance, in C the following lines have different semantics:
int a = 3;
inta=3;x = y++ + z; x = y + ++z;
For my money, I'll take Python's consistent use of ":" to start a block, though.
It would be awesome if Haskell had standard row-polymorphic extensible records though, like PureScript or Elm
- some_code... where vs let... in some_code: these two constructs play a fairly similar role in Haskell. The problem with where is that it lets you introduce the value of a binding AFTER using said binding. Ugh.
- functions defined over several lines using pattern matching: I'm still not sure if it's a good idea or not, but I think overall a shorthand syntax like OCaml's "function" keyword (except less unfortunate) is better
- pattern matching and guards are separated (try going back to Haskell after trying a language which combines the two..)
-the absence of named and default arguments is an issue in a language like Haskell. This leads to many instances of "guess what the fourth argument here means"/"let's create a record type just for the arguments of this function" and elegant APIs such as function1, function1' and function2'' (good luck figuring out from the name only which function you need)
- a much stronger focus on brevity (single-name variables all over the place, horrible ASCII DSL galore) than on legibility
- I think the record situation situation improved since I stopped doing Haskell, but when I did, it was really a trainwreck (having to prefix the field names of all your records to prevent collisions is not a feature).
This is usually more readable as well. Consider:
λ: 3 `elem` [1,2,3]
"Is 3 an element of 1,2,3?"Also, div is not the same as (/):
λ: 3 / 2
1.5
λ: 3 `div` 2
1The things I don't like are:
- String vs ByteString vs Text - seriously tired of csing everythin
- Lazy by default is fun - but makes everything harder to optimize. What would happen if Haskell were strict? https://nikita-volkov.github.io/if-haskell-were-strict/
- Records are not namespaced - https://nikita-volkov.github.io/record/