data Maybe a = Nothing | Just a
...over this: data Maybe<a> {
Nothing,
Just(value: a)
}
I understand that programmers coming from mainstream languages might feel uncomfortable without all those angle brackets, curly braces, and parentheses (why do C-like languages have so many ways to syntactically group things?), so if this project helps them ease their way into Haskell I totally support it. However, a small part of the joy of Haskell is freeing yourself from the arbitrary syntactic boilerplate that most languages have.Many people put non-necessary parentheses in their mathematical expressions to help readers (and themselves!) understand them more easily. Sure, they could be left out, but this is not an optimization for the reader.
In your example, your version looks cleaner, but I find more complicated expressions far more readable with parentheses, brackets and all that stuff present in the second version. This is between angle brackets? This is a type! I don't have to parse the whole expression to infer this.
Expressions written with this "redundant" syntax are seekable. You don't need to read the whole book to pick the one thing you need to know and get on with your life. Redundancy also helps remove doubts on whether something is an error or is intended in complicated cases (good things these languages are strongly typed though, it helps a lot).
At the end of the day, it's all a matter of taste and habits.
Would math be usually written in an Haskell-like syntax, probably almost everybody would find it easy to read and beautiful. It's not, and therefore this kind of syntax probably (almost) only pleases people who are into these languages (because they've got used to it) and represents yet another obstacle for outsiders. I'd actually bet that this plays a role in the fact these languages are not more widely adopted. They make them look unreadable at first impression and people move on. It probably even prevent people from contributing to projects written in these languages. This is bad. I've been able to fix things written in languages I didn't know, but Haskell would probably be hard just because of its syntax, which is unfortunate.
On the other hand, it's good to see and develop new / alternative ideas, including on syntax matters.
f (g (h x))
f . g . h . x
x . h . g . f
x . h . g $ f
(f <> g <> h <> x) -- potentially more a type-dependant operation than <>
(f <> g <> h) <%> x -- For some operation <%>
Furthermore Haskell is only indentation sensitive whereas mathematics notation depends on alignment and structure in two dimensions more generally.Therefore I claim it is silly to talk about mathematics notation as if it were a single standard thing because the reality is that mathematics notation depends on context a lot and varies a huge amount by situation. It is not obvious that the kind of elementary algebraic notation one might encounter in high school is the best fit for programming which tends to deal with totally different things.
I actually think Haskell makes a pretty good language for a small group as it may be better moulded to the problem domain. I recall an example from a mailing list where someone defined:
(.) x f = f x -- for my poor OO brain
infixl 9 (.)
So they could write code like: prodSize xs ys = xs.length * ys.length
Which seems silly except that the user has adapted the language to fit themselves. Some mathematicians also prefer function application/composition on the right. It makes values pass through functions from left to right the same way that their types are written.Further, I'll go out on a limb and say that the benefit of reading has NOTHING to do with audience reception of the syntax (whether that audience is new to the language, or extremely experienced with it), and instead has everything to do with what seems familiar. (I.e., I think I'm in agreement with you in that it's what people's habits are as to whether it's appreciated or not; I'm just disagreeing that improved readability is a factor in whether people appreciate the extra boilerplate, or the possible implication that it's a net gain)
And coming up against arbitrary three-four-character combinations people come up with for their operators.
Terser does not automatically mean better (or J and K would've won that argument a long time ago).
At least mainstream languages are usually consistent. A method call is a method call. A structure is a structure. What is this ungodly mess?
type API = "polls" :> Get '[JSON] [Poll]
:<|> "polls" :> Capture "question_id" Int :> Get '[JSON] Poll
:<|> "polls" :> Capture "question_id" Int :> "results" :> Get '[JSON] PollResults type API = "polls" :> Get '[JSON] [Poll]
:<|> "polls" :> Capture "question_id" Int :> Get '[JSON] Poll
:<|> "polls" :> Capture "question_id" Int :> "results" :> Get '[JSON] PollResults
Delightful :D The API is the route "/polls" accessed via GET, returning a list of Polls in JSON format
or the route "/polls/question_id" where question_id is an Int, accessed via GET, returning a Poll in JSON format
or the route "/polls/question_id/results" where question_id is an Int, accessed via GET, returning PollResults in JSON formatNothing requires that though, it's more of a debatable community habit, similar to the overly abstract or cutesy names.
> At least mainstream languages are usually consistent. A method call is a method call. A structure is a structure. What is this ungodly mess?
Haskell is arguably way more consistent there: "mainstream languages" also have operators, often overloadable, you just can't declare your own operators in most of them.
Haskell simply allows sigils to be used as infix functions, aka binary operators, and furthermore allows those to be used as prefix functions, as well as prefix binary functions to be used infix.
An eDSL ? Much like every DSL, it requires to invest some time learning, and gives you something if you take that time. It's not really specific to Haskell.
Back when I was doing some Java, I had to learn the logic of maven xml files, and it was exactly the same kind of investment-for-future-reward.
/polls returns an array of Poll. /polls/:question_id returns a Poll. /polls/:question_id/results returns a PollResults.
the thing you call a mess is the combination of type level operators which helps you describe your API which is the essence of functional programming.
[As others have pointed out, you get the choice of layout in Haskell syntaxes and, as it happens, that Scheme syntax also allowed you to use sexp input.]
TL;DR math notation
Because they are different things. Brackets are for types, curly braces are for "bodies" (struct definitions, initializers, statements, function bodies, control flow construct bodies), parentheses are for overriding precedence.
There are obviously exceptions that don't fit in this general scheme: e.g. function call syntax is based on parentheses, so it's not just about precedence.
All this is just a post-fact rationalization; this all comes from standard math notation.
Math uses parentheses for function application. Math uses commas for arguments of functions of multiple variables. Math uses curly braces to state of things are defined (sets, inductive rules) etc. Math uses parentheses to override precedence.
All the rest is that historical accident. Angle brackets were unused so they choose them for types. Square brackets for array indexing? No idea why
Curly brackets may be used for sets but they are also used for different cases of functions, or as a more succinct and precise way of expressing sentences about multiple things than writing “foo, respectively bar” all over the place, or for Stirling numbers of the second kind, or for whatever other things an author might want.
Other “structure making” notations like group presentations use angle brackets. Ideals are sometimes written with their generators in angle brackets, sometimes with round brackets. Similarly tuples or sequences may be written with either.
Matrices are sometimes written with square brackets and sometimes round, and never with commas. Vectors typically are written with round brackets but may be square. Row vectors sometimes have commas.
Function application is sometimes written with round brackets. Arguments are sometimes separated by commas, sometimes by semicolons, sometimes with one argument as a subscript or implicit. Application often omits brackets altogether. Sometimes the function goes on the left, sometimes it goes on the right.
Now consider the few features which I find are relatively common to different mathematics notations are context, alignment and structure in 2 dimensions, juxtaposition, implicitness, and terseness. I think I see basically none of these in the common C/Rust style syntax you claim to be so rooted in mathematical notation, and indeed I would guess that you would oppose them.
I don’t buy your arguments at all.
PureScript always seemed a little more appealing from that perspective, but doesn't seem to have really caught on (eg; https://trends.google.com/trends/explore?date=today%205-y&ge...)
When Python adds new string formatters or generators or async, they're just in the language. When Haskell does that they require you to explicitly turn the new feature on. It's just a different strategy of managing language change. Versus the classical approach you get a lot more eyes on your language additions and have a nice "beta" period where you can make improvements before there is wide adoption.
This is basically the same approach that Rust is taking but they've been better about removing the feature flag when the feature is stabilized.
The developers of rustc define the language, while the developers of GHC are trying to be first an implementation of a purportedly independent standard.
I don't raise this as defense or attack on either side, but I think it's an important bit of context in understanding the differences.
And yes, I think the Rust comparison is a great one – I'm not the sort of person who would want to use nightly Rust and turn on feature flags; I'd want to use Stable rust with no extra features.
Googling "most popular haskell language extensions", I see this article: https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated... which lists 34 recommended extensions and admits "a few of them are likely to be more controversial than others". Half of them don't have names which make their purpose immediately obvious to me.
A more data-driven approach is turned up here: https://gist.github.com/atondwal/ee869b951b5cf9b6653f7deda0b... which concludes "only 10 extensions show up in more than 10% of the Haskell files on GitHub, and 20 show up in more than 5%!"
Its discussion on reddit (https://www.reddit.com/r/haskell/comments/916jj0/popularity_...) highlights some gaps in the analysis, but also the general notion that "everybody uses language extensions, but not everybody uses the same ones".
Of course, each language extension changes the syntax and/or semantics of the language, so in order to "learn Haskell" I'd have to learn one or more flavors/styles of it – and there doesn't seem to be one standard set of language extensions that are commonly used.
This makes me feel that, totally apart from the syntax, Haskell/GHC is still more of a laboratory for doing programming language research. Which is great for what it is; I would say Haskell's original motto "avoid popularity at all costs" still holds. I also had hope for languages like PureScript that make an opinionated choice about a subset of those features for the sake of a consistent development framework.
> Of course, each language extension changes the syntax and/or semantics of the language, so in order to "learn Haskell" I'd have to learn one or more flavors/styles of it – and there doesn't seem to be one standard set of language extensions that are commonly used
> It seems like every Haskell project makes use of a different subset of 10 to 20 language extensions, each of which requires some deep knowledge of type theory to understand how it affects the language.
These comments are both pretty off the mark. It's unhelpful to think of language extensions (the most commonly-used ones at least) as extending the language. It's more that there is one overall Haskell language and the absence of a particular language extension disables a particular feature. The language is, by and large, more coherent with language extensions turned on, not less coherent.
> easier to refactor and automatically indent/format code.
What are the advantages of white-space insensitive code for refactoring and formatting?
For refactoring, it's less error prone when you're using an editor without refactoring support for it, would be my guess.
For indent/format, your guess is as good as mine, AFAIK programs don't care whether block separators are called INDENT or {. Though the parsing of the latter would be somewhat trivially simpler as you don't need to store a stack of indent levels.
For formatting, it means I never need to manually add any spaces or tabs: the formatter always knows exactly how to indent it. With whitespace-sensitive code, I have to manually pick the indentation, as different indentations could have different semantics. That's why if I hit tab in Emacs when editing a Haskell file, it will cycle through multiple possible indentions. If I hit it in a whitespace-insensitive language, it will just move it to the "correct" indentation straight away and I don't need to worry about it.
Using {} braces in Haskell seems less intuitive, at least to me. E.g. how would I use braces to avoid the need to manually indent a nested where statement?
it's such a beautiful thing ..
fib = cases
0 -> 0
1 -> 1
n -> fib (drop n 1) + fib (drop n 2)
(drop is (-) for natural numbers, clamping to 0.) fib = \case
0 -> 0
[...]Given that Haskell is fundamentally and irreparably broken (IMHO) in this way, it seems like a waste to devote effort to it instead of better functional languages like ML.
Like in the example of
f = average . filter (> 0) . map normalize
vs let f(lst) = lst.map(normalize)
.filter((x) => x > 0)
.average
In the second version I can clearly see that I'm creating a function that is supposed to take one argument. It even has a name that might indicate what I'm expecting.Don't get me wrong. All the required information is there but some of it is "written between the lines".
Sort of like CoffeeScript which people tend to hate (and I love, just because I understand JS perfectly and in comparison to CoffeeScript it looks noisy for me).
let f lst = lst
& map normalize
& filter (\x -> x > 0)
& average
Which is barely different from your second presentation. (EDIT: just in case you think I'm ass-pulling here, this is fairly idiomatic code that I'd certainly write in prod! See another comment I posted earlier this week...)I'd call it readable haskell not hinc.
Most popular languages created sort of universal visual language of what things mean when they look certain way and weird languages with very cool mechanics like Haskell, have much trouble with conveying this mechanic to the new user when they don't subscribe to this visual language (for historical or whatever reasons).
python kind of ignored this "universal visual language", and the language seems pretty popular with newcomers.
Coming from a C-style language background, I've always found the syntax of languages such as Haskell and ML to be alien, to the point that I can't get my head into learning one of them, despite their obvious power and utility.
I've been looking for some kind of C-style dialect for one of these languages for a long time, so thank you very much for creating this.
At the risk of hijacking this thread; is anyone here aware of other projects for Haskell or other languages that achieve a similar goal?
That said, I really appreciate seeing work like this. Haskell is super powerful and it's great to see transpilers like this that are lowering the bar of entry to Haskell to those more familiar with the traditional languages. Nice job!
> For example, where Haskellers would write:
f = average . filter (> 0) . map normalize
> in hinc the idiomatic translation would be: let f(lst) = lst.map(normalize)
.filter((x) => x > 0)
.average
These seems like very sane, pragmatic choices considering the massive segment of programmers used to C/Java/JavaScript derived syntax.Now, if the currently top comment about category theory could be addressed, I think we'd have something.
Its a shame that F# doesnt gets the attention it deserves.
Maybe the 'data' could be called 'enum'? I believe Rust/Swift ADT definition use keyword 'enum' and it might make it more familiar to the mainstream.
While the 'await' is pretty recognizable, the word itself seems to be too coupled with asynchronous operation too much.
This is way better, but to be honest I don't think I'll use it because the extra easiness of learning with a sane syntax is probably going to be not worth the extra hassle caused by not learning the syntax that everyone else uses.
Still, great job! Now do the git CLI. :-)