7,375 karma · joined February 21, 2007
Same user did a similar thing by creating an AWK interpreter written in Go using LLMs: https://github.com/kolkov/uawk -- as the creator of (I think?) the only AWK interpreter written in Go (https://github.com/benhoyt/goawk), I was curious. It turns out that if there's only one item in the training data (GoAWK), AI likes to copy and paste freely from the original. But again, it's poorly tested and poorly benchmarked.
I just don't see how one can get quality like this, without being realistic about code review, testing, and benchmarking.
def do_foo(sleep=time.sleep):
for _ in range(10):
sleep(1)That is probably my favourite phrase from the whole book. For some reason I find it hilarious. It has stuck in my brain in much the same way that names don't.
I'm curious what you're referring to there. I think of Go as the "anti-Java". Where Java has "org.apache.commons.httpclient", Go has "net/http". Where Java has stuttering like "final Logger logger = LogManager.getLogger();", Go has "logger := log.New()" (and no log4j vulnerablity). A typical "simple" app in Java puts files in "src/com/username/simplewebapp/subdir", typical Go project structure is to put files in just "subdir", or even just in the root dir. And so on.
Example from the sqlc creator (https://github.com/sqlc-dev/sqlc/discussions/364#discussionc...):
-- name: FilterFoo :many
SELECT * FROM foo
WHERE fk = @fk
AND (CASE WHEN @is_bar::bool THEN bar = @bar ELSE TRUE END)
AND (CASE WHEN @lk_bar::bool THEN bar LIKE @bar ELSE TRUE END)
AND (CASE WHEN @is_baz::bool THEN baz = @baz ELSE TRUE END)
AND (CASE WHEN @lk_baz::bool THEN baz LIKE @baz ELSE TRUE END)
ORDER BY
CASE WHEN @bar_asc::bool THEN bar END asc,
CASE WHEN @bar_desc::bool THEN bar END desc,
CASE WHEN @baz_asc::bool THEN baz END asc,
CASE WHEN @baz_desc::bool THEN baz END desc;Peter Weinberger (quoted with permission) responded:
> That's interesting, Here's some thoughts/recollections. (remember that human memory is fallible.)
> 1. Using whitespace for string concatenation, in retrospect, was probably not the ideal choice (but '+' would not have worked).
> 2. Syntax choices were in part driven by the desire for our local C programmers to find it familiar.
> 3. As creatures of a specific time and place awk shared with C the (then endearing, now irritating) property of being underspecified.
> I think that collectively we understood YACC reasonably well. We tortured the grammar until the parser came close to doing what we wanted, and then we stopped. The tools then were more primitive, but they did fit in 64K of memory.
Al Aho also replied (quoted with permission):
> Peter's observation about torturing the grammar is apt! As awk grew in its early years, the grammar evolved with it and I remember agonizing to make changes to the grammar to keep it under control (understanding and minimizing the number of yacc-generated parsing-action conflicts) as awk evolved. I found yacc's ability to point out parsing-action conflicts very helpful during awk's development. Good grammar design was very much an art in those days (maybe even today).
It's fun to hear the perspectives of the original AWK creators. I've had some correspondence with Kernighan and Weinberger before, but I think that's the first time I've been on an email thread with all three of A, W, and K.
What a great book! Zinsser uses such crisp, engaging prose. He covers how to write, why to write, and what to write. He uses lots of examples from his own writing and from others -- good and bad. Stories abound. This is not a boring grammar book.
Here's a taste -- he starts the chapter called "The Lead and the Ending" with these stark statements: "The most important sentence in any article is the first one. If it doesn't induce the reader to proceed to the second sentence, your article is dead." Punchy and self-referential.
There's a decent amount about good technique, but it's more about style and voice. The second half of the book covers topics like "Writing in Your Job" (without sounding passive or using buzzwords) and "Writing Family History and Memoir" (that's actually why my brother lent me the book). And it finishes with an inspiring chapter on the craft of writing called "Write as Well as You Can".
If you want to learn to write better, read this book. If you don't, definitely read it.
There, fixed the title.
As far as this book goes, when I read it a few years ago I kind of appreciated it, but found it a bit too abstract and philosophical to be useful.
var err error
var leftSideTableExists
tm.leftTbl, leftSideTableExists, err = rm.left.GetTable(...)
if err != nil { ... }
if leftSideTableExists { ... }
Plus, the code explicitly defines the variables with "var" instead of ":=", making it more verbose, and it overwrites tm.leftTbl even if GetTable returns an error (doesn't matter in this case as tm is locally-created, but is often not desired). I'd probably just call it "exists", but if you do want to distinguish left-exists and right-exists, I'd use "leftExists"; "side" and "table" are obvious from the context: left, leftExists, err := rm.left.GetTable(...)
if err != nil { ... }
if leftExists { ... }
tm.leftTbl = leftAs someone who has written a ton of Python and a ton of Go (and likes both languages), I still prefer Python for small scripts due to its terseness and beautiful syntax.
Side note: Go's static typing is so much better than Python's type annotations. Python typing is difficult to use and a mess: it's constantly changing ("improving"), Pyright and MyPy never agree, Pyright doesn't agree with itself between versions, and much more. The bolted-on, late-in-the-game nature of Python's typing really shows.
One place where I have a real side-by-side comparison of the two languages solving the same problem is: I wrote a streaming websocket client with both Go and Python. Go's was so much easier to reason about due to the simplicity and ubiquity of io.Reader and io.Writer. Compare that to the mess that is Python "file-like objects" (https://docs.python.org/3/library/io.html). Should I use BufferedIOBase, or TextIO, or maybe RawIOBase, and what do I override in my subclass? It was quite painful.
My take is that, in addition to its speed, Go wins on its type system, I/O interfaces, concurrency, package management, and test/benchmark tooling. Python wins in its beautiful syntax, [list|dict|set|generator] comprehensions, and quantity of 3rd party libraries.
In addition, the official 1BRC explicitly evaluated results on a RAM disk to avoid I/O speed entirely: https://github.com/gunnarmorling/1brc?tab=readme-ov-file#eva... "Programs are run from a RAM disk (i.o. the IO overhead for loading the file from disk is not relevant)"
$ time ./t
Hello, world
real 0m0.003s