Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Not that you would necessarily use the same words in Haskell, but I'm curious how you would read `user1^.name` in Pascal, or `user1->name` in C or `(*user1).name` in C?

(Edit: I'd also be curious about `x += y` and `x << y` in C.)



user1^ is what the user1 pointer points to, if I remember my Pascal correctly. It's a structure and the .name says to take the name field of that structure. So ^. isn't a digraph, it's two separate operators.

Same with your C example. It's doing the exact same thing in C, except the operators (* and .) are separated from each other.

-> is a digraph. It means the same as `*.` I don't usually pronounce it, I just think of it as itself. If I have to say it to myself mentally, I say "sub". I'm not sure I've ever tried to say it aloud to a coworker; if I did, I might have said "arrow" or something.

You do have to learn these things, just like you have to learn all the other operators. But simiones still has a point - at least the math-based operators are much more widely known and understood than the category-based ones.


But what are their names? That was the standard applied to Haskell.. that the operators needed well understood names. C++ does not have that. Worse still, C++ operators are completely unintellible without context.

For example, '>>=' (commonly called bind in haskell) is well-specified. Anything I see it used with is going to be a Monad, and thus follow certain laws. Examining the imports, I can immediately tell what any operator is.

In C++? Forget about it. The 'left-shift' operator which is supposed to shift bits to the left, can somehow also print things to standard output. In what world can the terminal be bit-shifted left? In fact, we understand this because no one reads 'std::cout << "Hello world"' as 'shift std::cout left by "Hello world", because such a thing is non-sense, whereas '1 << 2' is '1 shifted two bits to the left'.

EDIT: And then, when you add in external libraries, it gets worse. `<<` can be used for creating lexers and parsers in boost if I recall correctly. Completely lawless, and, when you survey the ecosystem, also dangerous. So many bugs in C++ and such due to this.


I agree. I do feel there is a double standard here. C gets away with using `->` and `^=` and `*foo.bar` mixes prefix and infix and postfix with hard to remember precedence rules; C uses `{` and `}` instead of `BEGIN` and `END` and no one bats an eye. But when lens libraries use operators, suddenly some people lose their mind.

I perfer `rec^.field` with operators in lens for the same reason I prefer `ptr^.field` in Pascal over hypothetically writing `ptr DEREFERENCE ACCESS field` or `ACCESS(DEREFERENCE(ptr),field). It lets me hide what is essentially just plumbing-like-grammer behind operators in order to let me focus of the parts of the program related to the business logic at hand, namely `rec`, `ptr` and `field`. Otherwise the plumbing tends to drown out the more important parts of what is going on.


First of all, some people's eyes already glaze over when looking at C. It's only accepted because it has become near ubiquitous in computing, and notation is much more palatable when it is consistent for a whole domain. This is a very important point that many people miss: the reason why Java, C#, JS, even C++ to some extent get away with lots of non-maths operators is exactly because they have chosen to copy each other. That's a very powerful thing.

Second of all, even within C, these operators are ubiquitous. You will use . and -> and * to deref and & to take the address, and = for assignment, and ++ or -- for increment/decrement, and ==, &&, ||, ! for logical operators, and the <operator>= notation for applying a change to a variable, and [] for subscripting, in almost every program you right in C. Shortening a very common operation to a symbol is much more easily acceptable than shortening a more rarely used operation, or one that's specific to a library.

Basically, the status quo in programming as I see it is this: it's OK to use symbols instead of words for (a) syntax in your language (C curly braces, dots), or (b) ubiquitous constructs that are used virtually all over any program (+=, *), or (c) if they are already well-known symbols in other popular languages, or in non-programming fields (such as the near-universal regex syntax). Even (a) and (b) benefit a whole lot from familiarity with other languages, as should be expected. They also still require proper names - which the C standard for example gives for every operator, as do C tutorials.


Thanks for all your replies. While I still think it is better to use operators in order to reduce clutter in the code, after all in order for any notation to become ubiquitous it must go through a period of not being ubiquitous, perhaps more can be done to introduce the operators in the documentation. It should be pretty easy to give all the operators names since almost all of them already have a named function implementing them.


Note that the C and C++ standards have names for all of these operators. "->" is called the "member access" operator (though so is ".", to be fair). It also has the advantage of being a pretty clear typographic symbol - it can be called "arrow".

As for <iostream> overloading the left shift operator to use for writing to an ostream, that is widely regarded as a terrible idea, and such overloading is generally frowned upon in the community. In general, <iostream> was created before C++ was even standardized, and well before good practices became established, and doesn't reflect at all more common usage of the language (see also the fact that they don't throw exceptions by default).

The only other somewhat widely used example I know where operators are overloaded with entirely different meanings than their standard usage is indeed in the boost::spirit parser generator library, where they are trying to approximate the syntax of typical parser definitions using C++ operators, with at best mixed success. And they don't just overload <<, they overload all the operators with completely different meanings - such as overloading the unary plus (+x) operator to mean "token appears 1 or more times" and even the structure dereference operator (x) to mean "token appears 0 or more times" and so on. Still, I don't think too many people are crazy enough to mix boost::spirit with regular C++ in the same file.

Note also that operator overloading is not commonly supported in other C-family languages, typically because* they are trying to avoid C++'s usage of it.


What are their names? "Member access" is the name of "->". That's a pretty well-understood and easily-understood name.

> Worse still, C++ operators are completely unintellible without context.

Um, compared to what? The C++ operators require less context to understand than the Haskell lens operators by a large amount, so this seems like a really odd criticism.

Your last two paragraphs... yeah. Operator overloading has been used for some, uh, unusual uses, even by the standards committee.


> Um, compared to what? The C++ operators require less context to understand than the Haskell lens operators by a large amount, so this seems like a really odd criticism.

This is my exact point. The C++ operators require much more context.

Suppose you see:

`v << i`

in c++. What is the type of v? What is i? Is it an int? Or an output stream? Or something else entirely! I have no idea. By the C++ standard it could be anything. In fact, it is heavily dependent on what's in scope, and indeed, several different things could overload it depending on what's in scope. Very confusing.

In haskell,

'x ^. i'

tells you everything. 'x' is any type, but 'i' is mandated to relate to 'x' in that 'i' is an optic targeting 'x' and returning some 'subfield' of x. In particular, 'i' is 'Getter a b', where 'a' is the type of 'x'. That is absolute. There is no other interpretation. Haskell type class overlap rules prevent any module imports from confusing the matter.


I agree with you about the context dependence being problematic in C++ (and that is also why overloading operators to give them entirely different meanings is deeply frowned upon, as I mentioned in other contexts).

However, I was curious - is it not possible in Haskell for two different libraries to introduce the same operator with different meanings, and still be used in the same program (though perhaps not in the same file)? That would be an unfortunate limitation all on its own, and one that mathematical notation certainly doesn't share.

In general, re-using symbols for similar purposes is also an extremely commonly used tool in mathematics, for the exact reason that constantly introducing completely new symbols is not worth the mental hassle. For example, when defining the "dot product" and "cross product" operations for vectors, entirely new symbols could have been introduced, but it was preferred to reuse common multiplication symbols (hence the names) as there is some relation to the real number multiplication operation.


> is it not possible in Haskell for two different libraries to introduce the same operator with different meanings, and still be used in the same program

It is possible, of course, just like it's possible for two different libraries to have a function of the same name. But it's always possible to determine statically (by looking at the import list, etc.) which of the instances the usage refers to.


Riiiiiiight...

I'll agree that overloading << to do output is a WTF moment. But at least I can hire programmers off the street who know what output is. In contrast, that last paragraph of yours looks like you were carrying around a bowl full of random jargon, tripped, and spilled it all over your keyboard.

For your C++ example, I have to know the type of v and i (and that in C++, it's possible to overload operators). For your example, I have to know what an "optic" is, what a "Getter" is, what Haskell thinks a "subfield" is (and that means that 'x' can't be any type, but has to be the kind of type that contains a subfield), and I suspect several other background things that I don't even know what they are.

C++ makes you know a bit more about the types (though even in Haskell, you had to know that x had subfields). Haskell makes you know much more concept-level context.


> In contrast, that last paragraph of yours looks like you were carrying around a bowl full of random jargon, tripped, and spilled it all over your keyboard.

It's scary that this sort of criticism is considered appropriate on hacker news, which is supposedly a forum for professional software developers. Very sad. Everything I mentioned should be taught in 2nd or 3rd year computer science course (namely, everything I mentioned was about type systems, which is a standard topic most university programs will cover)


I agree. I find it especially odd, that people think that programming and computer science is a complex topic that takes years to learn and master, but complain about the appearance of jargon with an appeal to "ease of use".

If the concepts are the hard part to learn, then operators and technical speech are the least of your worries, but a mandatory part on the path to acquiring the art. The most important thing math has shown me, is that notation and precision of speech take a huge part in making things clear. Yet people get heart attacks when this is applied to informatics.


> For your C++ example, I have to know the type of v and i (and that in C++, it's possible to overload operators). For your example, I have to know what an "optic" is, what a "Getter" is, what Haskell thinks a "subfield" is (and that means that 'x' can't be any type, but has to be the kind of type that contains a subfield), and I suspect several other background things that I don't even know what they are.

I still think this appeal to familiarity is dangerous. You take your knowledge about C++ as obvious and standard, and disparage knowledge about Haskell as a bowlful of jargon. But no-one comes into the world finding C++ intuitive, either; it has to be taught. And teaching C++ without referring to operator overloading, or with other attempts to obscure the way its practitioners actually talk about and practice it, can only be supported so far. Similarly here—I suspect more of the people who code with lenses in Haskell use the symbolic operators than the 'word' operators. Though I have no data to back that up, obviously it's how the writer of this tutorial thinks about them. So why shouldn't they use the format that makes sense to them, and that they hope to teach people to use?


> that last paragraph of yours looks like you were carrying around a bowl full of random jargon

The jargon isn't random, it's professional. Have you seen technical documentation for plumbing works in construction projects? They've got this glossary to memorize: https://wpjheating.co.uk/the-complete-plumbing-glossary/


I haven't used Pascal, so I can't comment on that.

user1->name : "user1 arrow name"

(*user1).name : "value of user1 dot name"

x += y : "x plus equals y" or "x increases by y"

x << y : "x left shifted by y"; the C++ overload for output streams is a terrible one that also doesn't have a name.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: