933 karma · joined November 17, 2007
Why did this happen?
- We had small common framework that everybody used, at the very highest level (think application lifecycle management). That imposed some amount of consistency at the most basic run-the-program stage.
- The devs communicated openly, a lot, so there was some general consensus on what to do, and what not to do.
- The team at large was very suspicious of introducing new macros. You could do it, but you'd better have a really good reason.
- When I went to make the changes, I didn't have to worry about spooky-action-at-a-distance kinds of consequences anywhere NEAR as much as I do in other languages. Being strict with your state management, as Clojure strongly encourages, REALLY pays off here.
The actual problems I had were entirely related to the overall build system, the fractured nature of the source control, and figuring out who was responsible for what code once we were 3 reorgs deep. The code itself was remarkably resilient to all this nonsense.
The essential difference between nom/chomsky and tree-sitter is that the former are libraries for constructing parsers out of smaller parsers, whereas tree-sitter takes a grammar specification and produces a parser. This may seem small at first, but is a massive difference in practice.
As far as ergonomics go, that's a rather subjective question. On the surface, the parser combinator libraries seem easier to use. They integrate well with the the host language, so you can stay in the same environment. But this comes with a caveat: parser combinators are a functional programming pattern, and Rust is only kind of a functional language, if you treat it juuuuust right. This will make itself known when your program isn't quite right; I've seen type errors that take up an entire terminal window or more. It's also very difficult to decompose a parser into functions. In the best case, you need to write your functions to be generic over type constraints that are subtle and hard to write. (again, if you get this wrong, the errors are overwhelming) I often give up and just copy the code. I have at times believed that some of these types are impossible to write down in a program (and can only exist in the type inferencer), but I don't know if that's actually true.
deep breath
Tree-sitter's user interface is rather different. You write your grammar in a javascript internal dsl, which gets run and produces a json file, and then a code generator reads that and produces C source code (I think the codegen is now written in rust). This is a much more roundabout way of getting to a parser, but it's worth it because: (1) tree-sitter was designed for parsing programming languages while nom very clearly was not, and (2) the parsers it generates are REALLY GOOD. Tree-sitter knows operator precedence, where nom cannot do this natively (there's a PR open for the next version: https://github.com/Geal/nom/pull/1362) Tree-sitter's parsing algorithm (GLR) is tolerant to recursion patterns that will send a parser combinator library off into the weeds, unless it uses special transformations to accommodate them.
It might sound like I'm shitting on nom here, but that's not the goal. It's a fantastic piece of work, and I've gotten a lot of value from it. But it's not for parsing programming languages. Reach for nom when you want to parse a binary file or protocol.
As for chumsky: the fact that it's a parser combinator library in Rust means that it's going to be subject to a lot of the same issues as nom, fundamentally. That's why I'm targeting tree-sitter next.
There's no reason tree-sitter grammars couldn't be written in an internal DSL, perhaps in parser-combinator style (https://github.com/engelberg/instaparse does this). That could smooth over a lot of the rough edges.
However, for my own parser which is currently written in nom, my current plan is to port it over to tree-sitter. Its error recovery is completely automatic, and a fair sight better than anything I have time to do by hand.
To me, the point of literate programming is that you have a coherent (literate, if you well) document that explains how the program actually works, and the reason it's put together how it is. This is NOT an easy thing to write. It takes as much organization as the program itself. I found the document structure to be continuously in flux, as I updated the program to deal with new requirements. So either document would poorly structured, or I would spend a LOT of time keeping it good.
The experience completely cured me of Knuth-style literate programming, fwiw. It's really great for making a lasting artifact about a program that's completely done. But I can count the number of programs I've worked on like that on zero fingers. Even this one isn't really done, but the cost of updating the essay along with the code discouraged me from working on it any more.
Regardless, you're probably doing yourself a disservice if you're allowing things like that to take choices out of your toolbelt. Perhaps the worst offender is TLA+, where you have to write actual ascii art and latex inside your code (yes, I know model/spec). I despise it, to be clear, but it's still a pretty good tool and often the right thing to reach for
For trivial cases, kind of. But once you start to do anything remotely sophisticated, no. Everything you do in Rust is checked w.r.t. memory management, but you still need to make many choices about it. All the stuff about lifetimes, borrowing, etc: that's memory management. The compiler's checking it for you, but you still need to design stuff sanely, with memory management (and the checking thereof) in mind. It's easy to back yourself into a corner if you ignore this.
Have you found that to be sufficient? I recently tried doing some online tutoring, and I found that even with an audio connection, I had a lot of trouble telling if anything I said was landing at all. I came away from the experience rather disillusioned with the idea of trying to do this online at all.
From the site:
> As a conservative watchdog to the nation’s higher education system, Campus Reform exposes liberal bias and abuse on the nation’s college campuses.
The likelihood this is accurate is very low.
I believe that Cucumber is at its best in situations where it's clear to all parties that a specification is valuable. In that case, making the specification executable is very clearly a massively useful way to spend your time.
That's a really nice idea. It doesn't fit into the usual compositional design of the proptest libraries that I've used, but it certainly seems like it should be possible in principle.
- Atoms: very popular. Usually what people reach for, perhaps overly so.
- STM / refs: Not commonly used, but I wonder if this is because we aren't used to really considering them as an option?
- Agents: I think everybody agrees that you shouldn't use these
- core.async: What many people think you should use instead
- Reducers: I've never seen these used in the wild.
1) Follow only people you have met in person, or otherwise directly corresponded with. I make very few exceptions to this.
2) Disable all retweets. Twitter doesn't make this easy, but there are tools that do it in bulk.
3) Make sure you're looking at 'latest tweets' mode, so that you only see the content you chose with (1) and (2). Twitter very frustratingly will put you back in 'top tweets' mode periodically, so you'll have to keep an eye on this. It feels like this happens about every 2 weeks but I haven't measured.
With this setup, nearly all of the viral BS drops away. I only see tweets in which a person I have some relationship with has taken the time to actually type something. 'quote tweets' are still problematic, but it's not nearly as bad.