The tree data type in Rust could be:
enum Tree<A> {
Leaf(A),
Node(A, Box<Tree<A>>, Box<Tree<A>>),
}The tree data type in Rust could be:
enum Tree<A> {
Leaf(A),
Node(A, Box<Tree<A>>, Box<Tree<A>>),
} enum Tree<A> {
Leaf(A),
Node(A, Box<Tree<A>>, Box<Tree<A>>),
}
be more "familiar" syntax than data Tree a
= Leaf
| Node a (Tree a) (Tree a)
?Are we assigning here with the =? What does the pipe symbol mean? Why pipe instead of another =? Why the weird formatting?
To me Haskell's look is too off-putting. With the Rust example I have a good guess what will the resulting object will look like.
But I know it's just a learning experience. Once I would know Haskell your example looks more elegant to me. I just don't get it from looking at it :)
> Are we assigning here with the =?
Kindof you are assigning what the type `Tree a` is.
> What does the pipe symbol mean?
The pipe is symbolizing or/either here. A tree is either a Leaf or it is a Node with two subtrees.
> Why pipe instead of another =?
You are building up a single type with the pipes, having an extra = wouldn't really make sense when you are thinking about building a type algebraically.
> Why the weird formatting?
The formatting is optional. It is free to be all in one line if you want it that way. For the given example, I would probably make it a single line, but I'm not a Haskell veteran.
You could ask many of the same questions of Python's non-C/non-Java like syntax:
- What is "def"?
- Why the weird formatting? (And unlike Haskell, Python's tends to be stricter!)
- Why do I need to write ":" after some lines but not others? It doesn't work like the semicolon in C-like languages!
- What's with the "if __name__ == '__main__'" weirdness I see in some Python programs?
- What's this weird [f(x) for x in ...] syntax? It doesn't look like anything in C. What's with the brackets anyway?
Etc.
Yet Python with its "weird" syntax and constructs is a hugely popular language...
https://www.google.com/search?q=haskell+pipe+operator&oq=has...
https://www.google.com/search?q=__name__+%3D%3D+%27__main__&...
Do note Haskell tutorials and communities abound, and you have excellent online tools such as Hoogle (in which you write the type of what you think you want and it responds with "these are functions with a similar type signature, with their documentation"). It's easy to google Haskell things, just not as easy as googling Python things :)
Do note the type definitions from the example are Haskell 101 and will be covered very early in almost every tutorial, for example Learn you a Haskell.
PS: it's not a "pipe operator" you're looking for. This isn't an operator at all! The "|" you're looking for it's in a definition, and it means a union of alternatives (this type can be "this" or "that" or "this other thing"). If you think about it, this "union-or" is written the same as the bitwise-or from more popular languages :)
In the Rust syntax ',' means both OR and AND.
A Tree {"IS" a Leaf ,"OR" a Node
A Node ("IS" an A ,"AND" a Box ,"AND" a Box.
Looking at the second definition it's not immediately apparent what 'a' is and "Node a (Tree a) (Tree a)" seems just like a bunch of words concatenated by spaces, it has no apparent structure or meaning, unless you're used to writing Haskell/ML/Lisp/etc.
That's false. A programmer coming from Python or Go won't know this. Nobody who hasn't been exposed to the extremely arbitrary generics syntax in Java-like languages will know about <A>.
I'd be interested to know if anyone has done interesting conceptual and/or empirical work on readability. It seems like a very slippery and difficult concept to me. Readable to whom? Readable in the small or the large?
I think there is work on "readability", just in another context: Its called typography and orthography. And I think the gist of it is: Do it like everybody else does, first and foremost, strange and unfamiliar equals unreadable.
I think that's a very different case. Maybe some analogies might be drawn between some of that work and some of the lower-level aspects of reading code (related to syntax noise etc), but code readability, if it's a defensible concept at all, is a far more complex and layered phenomenon than letter & word recognition.
The first thing a researcher would need to establish is whether or not readability even exists as a natural kind apart from familiarity. I don't know the field, so this might already have been pursued somewhere.
Agreed. Note the same applies to Haskell's syntax :)
People confuse "readable" with "based in my knowledge of Java and C, I can't make head or tails of this notation without reading a tutorial first", which in my opinion is not a sensible conclusion.
GP should note that C++’s unique_ptr isn’t an obscurity.
It doesn't matter if some people are confused, because you can just explain what it is in 3 seconds. What's important for such a ubiquitous type is that the name is short.
That would be very misleading since Box represents a heap-allocated owned value
They are tagged unions, but sometimes, the tag doesn’t exist. Or rather, invalid parts of values can be used so that the tag isn’t an extra bit of data, but instead is built into the same space. “Tagged union” gets too deep into only-mostly-accurate implementation details to be a good name.
Sure, tagged union implies a particular implementation that may not always be required, but it's conceptually easy to understand and doesn't have the historical baggage. (Maybe a better description would be strongly typed union? I want to make clear I'm unfamiliar with Rust and just guessing based on the syntax presented.) I think the biggest problem with "tagged union" (or the even longer "strongly typed union") is that it just isn't a good keyword name — it's two (or three) words and fairly long. No one wants to type out 'tagged_union' and from that sense, 'enum' is better. I don't have a better suggestion for you, and IIRC Rust 1.0 has now frozen the language to some extent.
Thanks for trying to explain, I appreciate it.
I think also, likewise, “union” sounds strange unless you have C experience. Many of our users do not know C, and so that name doesn’t help them either.
In the end names are hard.
Happy to, you’re very welcome :)
Indeed! Thanks again.
The rust example to me isn't immediately obvious that it's an either / or situation other than that must be how an enum works (I know enums from other languages)
Node(A, Box<Tree<A>>, Box<Tree<A>>),
is obviously nested (a Node contains an A and two Trees) and the trees are optional (they are contained in a Box, whose purpose is obvious without even knowing the language) | Node a (Tree a) (Tree a)
might or might not be nested, given the cheerful taste for currying and juxtaposition without punctuation that prevails in Haskell syntax, and it isn't obvious what purpose the parentheses serve (just grouping ?) and whether the trees are optional.You need to know that “enum” means that commas in the next section are different than usual, but only one layer deep—check nesting carefully. You need to know about type parameters in either version.
I had a similar experience learning my first ML: I couldn’t even tell how many words each word would gobble up, because I didn’t know the reserved words get. Syntax highlighting helped, and it’s not a problem after a week or two. It’s no worse than figuring out what’s a binary vs unwary operator in C and its descendants.
Also: it’s not obvious to me that Box means optional/nullable. I’d expect it to mean a required non-null pointer to a heap element.
Currying makes no sense in type definitions. It's like saying that in Java you aren't sure if "String name" will run something, "given Java's cheerful taste for running things".
To me, it's obvious the parentheses in "(Tree a)" are grouping things, which is the most immediate (and correct) interpretation, but I'll agree this is more debatable.
(x + 1) / 2
do you assume part of it is optional?This is actually one of my main two beefs with Haskell:
. The language is really interesting from a feature point of view, but the syntax, oh man. Looking at Haskell code when you switch back and forth from a more conventional language (C/C++/JS/Java/C#/Python/etc...) is a major headache. I can switch from C++ to Python without a second thought. Not so with Haskell.
. You can't really predict how something will actually execute, that's left to the language to decide. While in some situations this is a desirable feature, in many, it isn't at all, especially if you're writing performance critical code.2. You can't really predict how something will actually execute, that's left to the language to decide. While in some situations this is a desirable feature, in many, it isn't at all, especially if you're writing performance critical code.
> You can't really predict how something will actually execute, that's left to the language to decide.
If this is referring to order of evaluation, it's better to think that it's up to the code to decide, not the language. In a function application/call like:
f a b c
it is f, not Haskell, that will decide in what order a, b, and c will evaluate by the way in which it uses them. Haskell, the language, merely doesn't force the evaluation of arguments before evaluating a function.