For starters, syntax drives how I interact with a language as much as - maybe more than - semantics. How expressions are laid out is intensely important to me, as it affects how I remember and visualise the code. I can visualise the layout of code I have not worked with in years when the syntax is clear, and the code is well formatted.
I can work around painful semantics and find ways to pretend they don't exist by avoiding features or picking patterns that work better; but painful syntax usually stares me in the face ever moment I work with a language.
I have more than once rejected or picked languages based on syntax. E.g. I can't look at a Python program without getting annoyed with the syntax, and I avoid using the language whenever possible over it, and I work with Ruby whenever I can for the same reason (though the language geek in me wants to cry whenever I think about the Ruby grammar)
I also reject the idea of avoiding hand written parsers to start with. I sympathise a bit with the idea. I can see quickly testing changes with a parser generator. And certainly, if you hand write a parser, you need to avoid the temptation of adding all kinds of awful exceptions.
E.g. I love Ruby as a user of the language, but the MRI parser is beyond awful, and I think the syntax could have had most of the nice aspects and avoided most of the awful syntactical warts with a bit more discipline ("favorite" wart at the moment: '% x ' parses to the literal string "x" - "%" when not preceeded by an operand that makes it the infix operator "%" starts a quote-sequence where the following character indicates what the quote character should be - with the exception of a few special character, most characters will set the quote character to its identity. So in '% x ', the quote character is space).
Though MRI uses a Bison parser, but contains thousands of lines of handwritten exceptions, demonstrating both the bad parts of hand writing irregular exceptions into parsers, as well as how easily you can mess things up even if using a parser generator if you have one that isn't strict enough.
But to me, if your hand written parser becomes big and/or problematic to maintain, you're designing a language that will be problematic to parse cleanly, and it's probably worth revising your grammar (I wish this rule had been adhered to for Ruby).
Nice, regular, clean grammars tend to lend themselves very well to small, compact hand-written parsers. In practice I've never run into a situation where a grammar change required major rewrites of a parser in any project I've worked on for this reason, unless the rule deviated majorly from what I'd consider good practice in language design in ways that would cause problems for most parser generators too.
Modularising a hand written parser along the lines of the grammar rules is easy, and few changes cut so deeply across grammar rules to make this difficult.
But what a hand written parser tends to get you over a parser generator, is better ability to do clean error reporting, and better ease of introspecting how parser changes actually changes the processing in ways that are meaningful to mortals. To me at least, this is a lot more difficult to do with ever parser generator I've tried (and I keep hoping to be proven wrong; I've tried writing my own too, to try to prove myself wrong, and so far I've failed to come up with something I consider a usable replacement to handwritten parsers - you certainly can come up with something expressive enough, but it tends to end up being verbose enough to lose most of the benefit over clean code in the target language that saves you from having to deal with idiosyncracies of the generator).
To me the "solutions" offered demonstrate exactly why syntax matters to me:
I deeply admire Forth and Lisp and descendants on a technical level, but the syntax has always been a massive barrier to me for both language classes. I chose a s-expression inspired syntax to kick off my own compiler project by basically treating it as a serialization format for the parse tree, and first adding a parser on top later, but I did that first to be able to toy with semantics of something I didn't intend to make into its own language, and then to act as the "guts" of my in-progress Ruby compiler, not because I'd be willing to work with it more than that.
If anything, I've found it incredibly painful to work with, and I'd never have "held out" for very long without bolting a more human-friendly parser on top very early on. The experience has made me more insistent - not less - on if not starting with the syntax, then at the very least co-evolving semantics and syntax from the outset.
> But what a hand written parser tends to get you over a parser generator, is better ability to do clean error reporting, and better ease of introspecting how parser changes actually changes the processing in ways that are meaningful to mortals.
Here is the thing: in a hand-written parser, we can more or less have a function corresponding to a grammar feature. And we can can trace the entry and exit of that function and put a breakpoint on it. And that's "`nuff said", pretty much.
You can do that with parser generators too. But I agree, in that too many parser generators do things like generate tables instead. In the past I've written generators that generate recursive-descent parsers that mimic how I'd handwrite them, and that is a lot easier to debug than table based generated parsers.
But the reason I've not pursued that more is that once you've added in various exceptions, and tree building, and error checking etc., you're not saving all that much over just hand writing the thing entirely.
One could consider how the Ruby parser would have looked like if the parser would have been built around a more powerful parser engine though. Would the developers have abused the added power to add even more exceptions or would the added power have made a lot of the exceptions go away?
(by more powerful I mean more powerful in the way the chart parser from the article is more powerful than an LR based parser, ie that it can parse a bigger subset of CFG in linear time).
It's hard to tell. The current Ruby grammar doesn't really need a very advanced engine per se. It certainly could have been written more cleanly even with Bison. It seems more like a case of painting yourself into a corner through in some cases.
Maybe a better engine would have led to more discipline, but I doubt it.
E.g. one of the things people tend to like about Ruby is the ability to leave out parentheses around arguments in method calls. People often disagree on where/when to use it, but it allows for DSL's that looks more "integrated" with the language, for example.
But it causes all kinds of weirdness around the edges. E.g. for starters, you need to look at the symbol table for the current scope to know whether to treat a given identifier as a reference to a local variable or a method call. In itself that's a tiny little infraction, but the reason it is necessary is to prevent big surprises in common cases, and the result is to leave a variety of surprises in more unusual cases.
If a valid local variable name has been assigned to, it's a local variable except in contexts where it would be a syntax error unless you treat it as a method call. If we assume "x=42" earlier in the scope, couple with the "fun" of the optional parentheses, you get stuff like this.
* "x" is a local variable.
* "x 1" is the method call self.x(1)
* "x + 2" is the method call x.+(2) (so on the object held in the local variable x)
* "[x 1]" is a syntax error.
* "y[x 1]" is a nested method call: y.[](self.x(1))
* "z(x 1)" is a nested method call: self.z(self.x(1))
* "z(x 1, x 2)" is a syntax error.
* "z(x 1, x(2))" is a nested method call, that tends to surprise people: self.z(self.x(1, self.x(2)) (most people tends to expect it will parse as self.z(self.x(1), self.x(2)))
* "z(x 1, x)" is a nested method call where "x" refers to two different things: self.z(self.x(1),42)
(with the caveat I've had too little sleep, so I might very well have messed up one or the other of these...)
It's not hard to parse, and not all that hard to understand and reason about, but it is messy, and tack on a number of them and the parser gets ugly, and you create odd "dark corners" of the grammar that most people never run into but that surprise them whenever they do, and that parsers for alternative implementations needs to get right.
> For starters, syntax drives how I interact with a language as much as - maybe more than - semantics. How expressions are laid out is intensely important to me, as it affects how I remember and visualise the code. I can visualise the layout of code I have not worked with in years when the syntax is clear, and the code is well formatted.
A good syntax could easily hide semantic warts that cause far more problems in real programming. If you focus on syntax too early, you're liable to introduce too many syntactic shortcuts to hide semantic ugliness when you should really address the problematic semantics.
For instance, Rust had lots of syntactic short hands to hide GC'd pointers, owned pointers, borrowed pointers and so on. These small syntactic conveniences produced some pretty code at small scale, but trying to compose independent programs using these concepts into larger systems caused all kinds of problems because of the non-compositional semantics. They ended up clarifying the semantics which led to dropping most of the special syntax for a clean core language, and now Rust is enjoying surging popularity.
Is it a good syntax if it hides important details? To me that sounds like a bad syntax. I want a readable and beautiful and clean syntax, but that to me also implies making important details clear.
The most important purpose of syntax is to ensure semantics are presented in a clear, readable and consistent manner. If it hides important information, it fails in that.
Note that the languages I've complained about the syntax of in this discussion, such as Lisp and Forth, have very minimal syntaxes, and it may be that they are too minimal, but it is not that minimalism in itself I take issue with, but that the minimalism sacrifices readability. There are languages with small syntax I admire, such as e.g. Oberon (grammar fits in a page and a half of BNF), where the syntax is very focused on clarity - my ideals for syntax are orthogonal to size/complexity.
> "favorite" wart at the moment: '% x ' parses to the literal string "x" - "%" when not preceeded by an operand that makes it the infix operator "%" starts a quote-sequence where the following character indicates what the quote character should be - with the exception of a few special character, most characters will set the quote character to its identity. So in '% x ', the quote character is space.
How is this different, in principle, from any other unary/binary operator like plus? In most languages, when `+` is preceded by an expression, it's a binary operator, otherwise it's a unary operator.
The same seems true here: when `%` is preceded by an expression it's binary `%`, otherwise it's a unary operator with the semantics you describe.
It's not horribly hard to parse. It is however ugly and surprising to almost everyone that sees it.
The difference between this and "+" is that if I present you with "+ x ", you know that this represents two tokens: "+" and "x". If you don't know what preceded it, you don't know if "+" is a prefix or infix operator, or what parse to return, but assuming the string starts at a token boundary, you can unambiguously tokenize it lexically without additional knowledge.
But for "% x ", you don't know in isolation if it represents the single token representing the literal string "x" or if it represents the infix operator "%" and the identifier "x".
It's an example of one of the features that prevents you from doing bottom up lexical analysis of Ruby without doing a full parse and pushing information down from the parser.
As I said, it's not hard - in the case of an operator precedence parser, if your value stack is not empty when you see "%", then you need to parse what follows as an expression. In the case it is empty, you need to parse it as a quoted string. There are a variety of ways to do that. But it's relatively uncommon for languages to be impossible to unambiguously tokenize without doing higher level processing.
I've also yet to see a single example of Ruby code where this freedom to pick pretty much any quote character has been used in a sensible way - or at all (I certainly have seen cases where "expected" quote characters have been used). Thankfully. So it's an unnecessary wart.
I am beginning to think there must be something about Ruby that poisons the mind in this way.
There is nothing (major) wrong with Forth, Lisp, or Python syntax, and if you think there is, it's just a prejudice that is slowing you down. Stop being so delicate and get over it.
Might as well use Cobol then, if you're that syntax agnostic.
I, on the other hand, can't stand the syntax of python or COBOL and it's one of the major reasons I avoid these languages.
If COBOL was the right tool for the job I wouldn't let the syntax, of all things, stop me. But that is hardly the greatest of COBOL's weaknesses at this point. An odd example.
If Python is otherwise a good choice for whatever you're doing and you're avoiding it just because of the syntax, that's an example of what I'm talking about and you should just get over it. It's just a change of perspective.
I think you're being kind of mean. I love Forth, Lisp, and Python, and I'd encourage people prejudiced against them to give them more of a chance, but I can also understand how a language can just rub you the wrong way. (Personal example: Go. It has a lot to like pragmatically, but it wants you to code in a particular style. I'd rather code in mine.)
The thing is I can see tremendous value in both Forth and Lisp in terms of the semantics, but in some cases we just have to accept that people favour different styles and cross-fertilise the ideas rather than try to make people use the same languages.
One of my old pet peeves that I wish I had time to pursue was to experiment with a language that tried to separate presentation from the semantic model - I know there has been other attempts. I wish that got more attention. It's a tremendously hard thing to get right without ending up hampering communication more than you enable it, but the need for high fidelity interchange of programs is one of the biggest barriers to more rapidly iterating on improvements to the presentation of code.
You can't design a language with Haskell's semantics and Ruby's syntax. So if you want to use Haskell, you just have to get over your dislike of the syntax. It has a beauty of its own, but you'll never see it until you get familiar with it.
> You can't design a language with Haskell's semantics and Ruby's syntax.
Not identical of course, no, but same style would not be a problem. There's nothing in Haskells grammar that can't easily be adapted to a less terse style. It wouldn't solve all issus I have with Haskell by far, but it would go a long way to make me consider it readable.
> but you'll never see it until you get familiar with it.
I am familiar with it. It is how I came to detest the syntax.
I look at it like someone who wants to learn French. Maybe they live in a French-speaking country and have French-speaking friends. When it comes to learning French, they decide they just can't stand the way it is spelled, and immediately give up. Wouldn't that be ridiculous?
Having learned Mandarin, I can tell you that giving up because of the way it is written would be a perfectly sensible thing to do.
However, we are comparing apples and oranges. Learning any language takes years, Mandarin a few more. The syntax of a programming language takes a few days at worst.
When someone invents a programming language where each of 30,000 library methods is a unique character that must be memorized separately, I'll be prepared to grant your point.
No, I will not "stop being so delicate". I know what I like to work with, and I know what slows me down, and I am in the lucky position of not having to suffer through languages I don't want to work with. (yes, suffer)
If you think this has to do with Ruby, you are showing your own prejudice.
I first played with Lisp, Scheme and Forth about 35 years ago, and didn't pick up Ruby until about 12 years ago.
If anything it was the opposite. I picked up Ruby because it was finally a language that provided similar power to a lot more austere languages packaged in a syntax I can mostly enjoy, and where the syntax mostly fit my way of thinking.
Yes, the syntax fits the way I'm thinking - how I write and structure the text of the code affect how I think about things, so I care very deeply about how I lay things out. To me the goal with any code is that it must in the end flow like a story or a poem, or I will be as frustrated as working my way through a badly written novel.
To me this is a major difference in how people engage with code. You have the camps that treat code as maths - you see it in extremes in languages like Haskell - and think of it in the abstract, and you have people (like me) who engage with it as exposition.
On top of that, as another dimension, you have the issue of to what extent people care about visualisation and aesthetics of the code. To me that is also important. I don't think of code in concepts, I think of it as visual blocks of exposition, carefully laid out to draw attention to specific words or phrases, like a poem, where shape can also matter.
Telling me to "get over it" is about as useful as telling me I should just start liking a dish I have already tried and know I hate the taste of. No. I have no reason to suffer for no reason.
If there is truly no reason, then I wouldn't tell you to get over it. But if there are other languages you'd be learning and using, and the only reason you don't is because of syntax, then you have a reason.
To take your example, Haskell can be poetic too, but until you become familiar with the syntax you'll never see it.
You keep assuming I'm not familiar with the semantics, and jumping to conclusion about my exposure to these languages without any basis. On the contrary it was through learning them I came to dislike the syntax. I have no doubt you can find Haskell poetic too, but it is not to me.
Still, though, you basically back's the article's the point "design syntax last". You like Ruby syntax and it has semantics you like; but the semantics was there first, then Matz came in with the syntax.
"Design the syntax last" does not mean that syntax is an unimportant afterthought.
I'm not saying that you shouldn't have semantics in mind, but no, I disagree with designing syntax last as much as I would with designing syntax first.
The syntactical decisions you make influence how you think about a problem as much as the semantic choices you make, and it needs to be a back and forth.
E.g. lisps homoiconicity fundamentally alter the freedom you have with respect to syntactic sugar (though I'd argue that it makes less difference than some want to think). If you leave the syntax considerations to the end, you will have potentially made semantic choices that constrain your syntax in ways you don't want.
This also applies in reverse.
You can't divorse the two.
The semantic concepts that made it into Ruby largely already existed, sure, but not the mix of them, and that mix is strongly affected by the syntax.
One effect of this interplay which I find a bit annoying (though I've looked long and hard for a solution that wouldn't be a grammar-nightmare), for example, is that while Smalltalk can (re)define core language features in a transparent way, Ruby can't, because while Ruby-style blocks can be very unintrusive, they are not invisible, so you can't simulate something like "if" or "while" without creating syntactic "noise".
These are conscious tradeoffs in Ruby - the cleanness of the core syntax was seen as more critical than retaining those features from Smalltalk. If the semantics had been designed first and dictated those features, then the current Ruby syntax wouldn't have been possible. We'd have gained some marginal utility, but we'd have lost readability.
OK: let's go with: design syntax any damn time you feel like it. Design it first and put finishing touches on it last. Design it while brushing your teeth or going to work, etc.
Don't give up too easily on Lisp. It also has picked up various syntax which has leveraged people's abilities.
As an exmaple, the backquote notation. Without it, it would be very painful to write macros at just one level of expansion. There is tremendous value in being able to do `(a b c (,@foo)) and so on, and it is a character syntax, like infix.
The possibilities for adding notations to Lisp without disturbing its syntax are not exhausted.
In my own Lisp dialect, I have introduced a modicum of notations which make quite a difference:
obj.slot.a.b.c -> (qref obj slot a b c)
obj.bar.(method arg) -> (qref obj bar (method arg))
a..b -> (rcons a b) ;; construct range object
[seq from..to] ;; slice
(fun arg . rest) ;; dot notation with atom serves as apply
(. blah) -> blah ;; dot not preceded by anything reduces to atom
#"word list literal" -> ("word" "list" "literal")
`quasi @literal @{blah[3]}`
All such little things make the "little code" you write daily a lot nicer.
It's understandable that coders might be put off by verbiage like (concatenate 'string ...) or (slot-value obj 'slot) or (aref array index).
You assume I gave up on it easily. I didn't. I simply found nothing important enough to keep suffer the syntax. Not because there's nothing of value in Lisp, but because most things of value have been copied by many other languages by now. I'm not saying that Lisp doesn't have value any more, but that what it provides that other languages doesn't is not sufficient to outweigh the inconvenience of the syntax to me.
> The possibilities for adding notations to Lisp without disturbing its syntax are not exhausted.
The problem with this is that while you can do it for yourself, until/unless it gains traction you still need to deal with the surrounding ecosystem too.
Furthermore, the semantics of Ruby are not unusual at all.
Being a scripting language in a class with Python and many others, Ruby basically competes on syntax and libraries/ecosystem. As long as the ecosystem is good enough, it's reasonable to choose Ruby over, say, Python for developing web apps purely because you like the syntax.
What I'm getting at is how people will avoid Scheme, Prolog, Haskell, etc. because of "syntax". Which is a loss, and a bit ridiculous as sufficiently different semantics require different, unfamiliar, "scary" syntax. Getting over that is syntax prejudice is just a matter of adjustment.
> Furthermore, the semantics of Ruby are not unusual at all.
Depends on what you consider "unusual". It's not unusual if you're used to Smalltalk, Self or Lisp, with Smalltalk being it's largest semantic influence. Given how niche they are, there are plenty of areas that are "unusual" to a lot of people.
> Which is a loss, and a bit ridiculous as sufficiently different semantics require different, unfamiliar, "scary" syntax.
Except they usually don't. There are aspects of this with the Lisp family, where homoiconicity is important, that makes different syntax harder, but even there s-expressions was not the originally intended syntax, and "modern" variations show that there is room for other variants even with that property.
Heck, my Ruby compiler uses s-expressions as an internal representation (that you can "escape into" from source - it's used to implement low level details).
EDIT: You keep falling in two traps: One is to assume that rejecting a language means rejecting the lessons in semantics it brings. Ruby is proof that this is not true - it's a collage of ideas taken from other languages, and predominantly from Smalltalk, another language where syntax is one of the things people balk at. And it contains plenty of lessons from languages that went before - e.g. it has lambdas, it has continuations. It even has call/cc. It's not perfect - that's not the point at all - but it demonstrates that you can "lift" these ideas from other languages and package them in another syntax just fine.
The other trap is to assume that because syntax is unimportant to you, it's unimportant to others, and we just can't be bothered.
This is like being deaf and jumping to the conclusion that just because you do fine without hearing, everyone else should just ignore the extra signals they get from sound.
To me the use of syntax signals intent, and give hints about semantics (when well done) and outline the model of a piece of code. It provides extra information that is lost if the syntax doesn't let me express it. To sacrifice would be like losing my hearing: I'd still be able to function, but why in the world would I willingly give that up?
If there was something that offered sufficiently compelling semantics advances, I might. This is one of the reasons I've spent thousands of hours learning additional languages. But here's the thing: Of all the several dozens languages I've spent time on, very few have been all that different when it comes down to it, and especially modern languages are increasingly borrowing most of the important semantic concepts. Most of the semantic concepts that have been "left behind" have been left behind for a reason (consider INTERCAL's wonderful "COME FROM"; an early example of support for aspect-oriented programming)
This leaves syntax as a much more important differentiator for most than you might think. You've written off Ruby as "not that unusual" on the basis of syntax, while e.g. not considering that Ruby's main lineage are niche languaes like Smalltalk. That Ruby doesn't seem all that unusual is a testament to syntax.
E.g. a concept like being able to execute code in a class definition (not just being able - even "def" is conceptually just syntactic sugar; you can write Ruby code using just the method call define_method instead) is something that seriously messes with peoples mind. That 'require' in most peoples code is not a keyword but a method call that has in most cases been overridden by either rubygems or bundler is something most people aren't even aware of.
It's not that these concepts are "unusual" in the sense that they're not there in other well known languages. Ruby certainly isn't another Befunge or False or Aardappel [1]. These concepts certainly are present elsewhere - this is part of Ruby's Smalltalk heritage. But they're not what most developers are used to. Ruby has brought those concepts into the mainstream in a way languages like Smalltalk never managed. That Ruby is not "unusual" is a feature, not a bug.
You fell into a trap yourself! Did I say somewhere that syntax is unimportant to me? I am as particular about syntax as anyone you are likely to find.
What I actually am saying is that missing out on the good things a language has to offer because of an inability to get over your dislike of the syntax is a weakness. If you have no taste, there is nothing to get over. That's not what I'm saying.
Yes, yes, I know how you "suffer". That's something you can get over.
I don't know your goals--if you program only to satisfy your own creative urges then it doesn't matter how you choose a language.
Assuming you work on things that other people also work on, then there is a cost to rejecting a language only because of syntax.
For example, if you are doing some work in an area where everyone is using Python numeric libraries, and you refuse to use Python because of fussiness about syntax, then you can't collaborate with those people. There's no good reason for that.
Reasonable people should be able to see when certain choices are arbitrary, and get over their attachment to arbitrary choices when it gets in the way of more important things, like collaboration.
Failure to do what I just described is endemic among programmers, to our shame.
For starters, syntax drives how I interact with a language as much as - maybe more than - semantics. How expressions are laid out is intensely important to me, as it affects how I remember and visualise the code. I can visualise the layout of code I have not worked with in years when the syntax is clear, and the code is well formatted.
I can work around painful semantics and find ways to pretend they don't exist by avoiding features or picking patterns that work better; but painful syntax usually stares me in the face ever moment I work with a language.
I have more than once rejected or picked languages based on syntax. E.g. I can't look at a Python program without getting annoyed with the syntax, and I avoid using the language whenever possible over it, and I work with Ruby whenever I can for the same reason (though the language geek in me wants to cry whenever I think about the Ruby grammar)
I also reject the idea of avoiding hand written parsers to start with. I sympathise a bit with the idea. I can see quickly testing changes with a parser generator. And certainly, if you hand write a parser, you need to avoid the temptation of adding all kinds of awful exceptions.
E.g. I love Ruby as a user of the language, but the MRI parser is beyond awful, and I think the syntax could have had most of the nice aspects and avoided most of the awful syntactical warts with a bit more discipline ("favorite" wart at the moment: '% x ' parses to the literal string "x" - "%" when not preceeded by an operand that makes it the infix operator "%" starts a quote-sequence where the following character indicates what the quote character should be - with the exception of a few special character, most characters will set the quote character to its identity. So in '% x ', the quote character is space).
Though MRI uses a Bison parser, but contains thousands of lines of handwritten exceptions, demonstrating both the bad parts of hand writing irregular exceptions into parsers, as well as how easily you can mess things up even if using a parser generator if you have one that isn't strict enough.
But to me, if your hand written parser becomes big and/or problematic to maintain, you're designing a language that will be problematic to parse cleanly, and it's probably worth revising your grammar (I wish this rule had been adhered to for Ruby).
Nice, regular, clean grammars tend to lend themselves very well to small, compact hand-written parsers. In practice I've never run into a situation where a grammar change required major rewrites of a parser in any project I've worked on for this reason, unless the rule deviated majorly from what I'd consider good practice in language design in ways that would cause problems for most parser generators too.
Modularising a hand written parser along the lines of the grammar rules is easy, and few changes cut so deeply across grammar rules to make this difficult.
But what a hand written parser tends to get you over a parser generator, is better ability to do clean error reporting, and better ease of introspecting how parser changes actually changes the processing in ways that are meaningful to mortals. To me at least, this is a lot more difficult to do with ever parser generator I've tried (and I keep hoping to be proven wrong; I've tried writing my own too, to try to prove myself wrong, and so far I've failed to come up with something I consider a usable replacement to handwritten parsers - you certainly can come up with something expressive enough, but it tends to end up being verbose enough to lose most of the benefit over clean code in the target language that saves you from having to deal with idiosyncracies of the generator).
To me the "solutions" offered demonstrate exactly why syntax matters to me:
I deeply admire Forth and Lisp and descendants on a technical level, but the syntax has always been a massive barrier to me for both language classes. I chose a s-expression inspired syntax to kick off my own compiler project by basically treating it as a serialization format for the parse tree, and first adding a parser on top later, but I did that first to be able to toy with semantics of something I didn't intend to make into its own language, and then to act as the "guts" of my in-progress Ruby compiler, not because I'd be willing to work with it more than that.
If anything, I've found it incredibly painful to work with, and I'd never have "held out" for very long without bolting a more human-friendly parser on top very early on. The experience has made me more insistent - not less - on if not starting with the syntax, then at the very least co-evolving semantics and syntax from the outset.