[HN Gopher] Don't Force Your LLM to Write Terse [Q/Kdb] Code: An...
___________________________________________________________________
Don't Force Your LLM to Write Terse [Q/Kdb] Code: An Information
Theory Argument
Author : gabiteodoru
Score : 83 points
Date : 2025-10-13 12:44 UTC (7 days ago)
(HTM) web link (medium.com)
(TXT) w3m dump (medium.com)
| icsa wrote:
| I think that there are a few critical issues that are not being
| considered:
|
| * LLMs don't understand the syntax of q (or any other programming
| language).
|
| * LLMs don't understand the semantics of q (or any other
| programming language).
|
| * Limited training data, as compared to kanguages like Python or
| javascript.
|
| All of the above contribute to the failure modes when applying
| LLMs to the generation or "understanding" of source code in any
| programming language.
| chewxy wrote:
| > Limited training data, as compared to kanguages like Python
| or javascript.
|
| I use my own APL to build neural networks. This is probably the
| correct answer, and inline with my experience as well.
|
| I changed the semantics and definition of a bunch of functions
| and none of the coding LLMs out there can even approach writing
| semidecent APL.
| sanjayjc wrote:
| > _I think the aesthetic preference for terseness should give way
| to the preference for LLM accuracy, which may mean more verbose
| code_
|
| From what I understand, the terseness of array languages (Q
| builds on K) serves a practical purpose: all the code is visible
| at once, without the reader having to scroll or jump around. When
| reviewing an LLM's output, this is a quality I'd appreciate.
| gabiteodoru wrote:
| I agree with you, though in the q world people tend to take it
| to the extreme, like packing a whole function into a single
| line rather than a single screen. Here's a ticker plant
| standard script from KX themselves; I personally find this
| density makes it harder to read, and when reading it I put it
| into my text editor and split semicolon-separated statements
| onto different lines: https://github.com/KxSystems/kdb-
| tick/blob/master/tick.q E.g. one challenge I've had was
| generating a magic square on a single line; for odd-size only,
| I wrote: ms:{{[(m;r;c);i]((.[m;(r;c);:;i],:),$[m[s:(r-1)mod
| n;d:(c+1) mod n:#:[m]];((r+1)mod n;c);(s;d)])}/[((x;x)#0;0;x
| div 2);1+!:[x*x]]0}; / but I don't think that's helping anyone
| krackers wrote:
| When Q folks try to write C: https://github.com/kparc/ksimple
| dapperdrake wrote:
| When EAX and RAX take too long to type.
| quotemstr wrote:
| Representative example: //!malloc
| f(a,y(x+2,WS+=x;c*s=malloc(y);*s++=0;*s++=x;s)) //!<
| (a)llocate x bytes of memory for a vector of length x plus
| two extra bytes for preamble, set refcount to 0
| //!< and vector length to x in the preamble, and return
| pointer to the 0'th element of a new vector \see a.h type
| system f(_a,WS-=nx;free(sx-2);0)
| //!< release memory allocated for vector x.
| G(m,(u)memcpy((c*)x,(c*)y,f)) //!<
| (m)ove: x and y are pointers to source and destination, f
| is number of bytes to be copied from x to y.
| //!< \note memcpy(3) assumes that x/y don't overlap in ram,
| which in k/simple they can't, but \see memmove(3)
| //!memory management f(r_,ax?x:(++rx,x))
| //!< increment refcount: if x is an atom, return x. if x is
| a vector, increment its refcount and return x.
| f(_r,ax?x //!<
| decrement refcount: if x is an atom, return x.
| :rx?(--rx,x) //!< if x is
| a vector and its refcount is greater than 0, decrement it
| and return x. :_a(x))
| //!< if refcount is 0, release memory occupied by x and
| return 0.
|
| Reminds me a bit of both the IOCCC and 70s Unix C from
| before anyone knew how to write C in a comprehensible way.
| But the above is ostensibly production code and the file
| was last updated six months ago.
|
| Is there some kind of brain surgery you have to undergo
| when you accept the q license that damages the part of the
| brain that perceives beauty?
| OneDeuxTriSeiGo wrote:
| Lol no. It's just Arthur Whitney style C (with the repo
| in question being originally written by Whitney).
|
| Whitney kind of set the stage for this but it got adopted
| informally as a style by the people at IPSA (I. P. Sharp
| Associates) and spread throughout the industry.
|
| Whitney style C isn't great for everything but it's not
| bad for interpreter writing and other text/stream
| touching tasks.
| lynx97 wrote:
| Hey, another language with smileys! Like haskell, which has
| (x :) (partial application of a binary operator)
| rak1507 wrote:
| There's a difference between one line and
| short/terse/elegant. {m:(x,x)#til x*x;
| r:til[x]-x div 2; 2(flip r rotate')/m}
|
| generates magic squares of odd size, and the method is much
| clearer. This isn't even golfed as the variables have been
| left.
| gabiteodoru wrote:
| Oh nice solution, thanks for sharing!
| gabiteodoru wrote:
| Do you know which method is this? Euler's by any chance?
| And do you have an idea how one would prove that it creates
| a magic square? It's actually one of my inspirations for
| writing this, the relationship between the code that does
| something and the proof that the code actually does what it
| claims. I'd argue an LLM would find the proof helpful if it
| were asked to generalize an existing function in some way
| torginus wrote:
| I've been dabbling in programming language design as of late,
| when trying to decide if including feature 'X' makes sense or
| not, with readability being the main focus I realized some
| old wisdom:
|
| 1 line should do 1 thing - that's something C has
| established, and I realized that putting conceptually
| different things on the same line destroys readability very
| quickly.
|
| For example if you write some code to check if the character
| is in a rectangular area, and then turn on a light when yes,
| you can put the bounds check expressions on the same line,
| and most people will be able to read the code quickly - but
| if you also put the resulting action there, your code
| readability will suffer massively - just try it with some
| code.
|
| That's why ternary expressions like _a = condition? expr1:
| expr2_ kinda controversial - they 're not always bad, as they
| can encode logic about a single thing - if said character is
| friendly, turn the light color should be green, otherwise red
| - is a good example - but doing error handling there is not.
|
| I haven't been able to find any research that backs this up
| (didn't try very hard tho), but I strongly believe this to be
| true.
|
| A nice thing is that some other principles, like CQRS, can be
| derived from this, for example CQRS dictates that a function
| like _checkCharacterInAreaThenSetLightState()_ is bad, and
| should be split up into _checkCharacterInArea()_ and
| _setLightState()_
| dzaima wrote:
| I'd perhaps generalize that to "it's useful to have visual
| grouping correlate with semantic grouping"; applies to
| separating words with spaces, sentences with punctuation,
| paragraphs with newlines, lines of code, UIs, and much
| more.
|
| An important question for this also is what qualifies for
| "single thing"; you can look at a "for (int i = 0; i < n;
| i++) sum += arr[i]" as like 5 or more things (separate
| parts), 2 things (loop, body), or just one thing ("sum").
| What array languages enable is squeezing quite a bit into
| the space of "single thing" (though I personally don't go
| as far as the popular k examples of ultra-terseness).
| torginus wrote:
| I'd say I'd decompose this into 2 'units'
|
| - iterate through the array arr,
|
| - sum its elements
|
| of course, since C is a low level language,
| implementation details seep in there but those are the 2
| major logical concepts that mentally can be quickly
| parsed as a single unit each.
|
| Some languages allow reductions on collections, so the
| actual iteration becomes logically part of that single
| unit, so it might count as one, but I'd say that's the
| absolute upper bound of complexity you should stuff into
| a singlwe line.
|
| The goal here is to make every statement conform to a
| template so that your brain quickly recognizes it and you
| can move on, rather than you having to break it apart bit
| by bit to figure out the goal.
| dapperdrake wrote:
| Perl and line noise also share these properties. Don't
| particularly want to read straight binary zip files in a hex
| editor, though.
|
| Human language has roughly, say, 36% encoding redundancy on
| _purpose_. (Or by Darwinian selection so ruthless we might as
| well call it "purpose".)
| 1718627440 wrote:
| Language is often consciously changed and learned, so it is
| sometimes quite designed.
| nlitened wrote:
| > Human language has roughly, say, 36% encoding redundancy on
| purpose.
|
| The purpose is being understandable by a person of average
| intellect and no specialized training. Compare with
| redundancy in math notation, for example.
| Qem wrote:
| > The purpose is being understandable by a person of
| average intellect and no specialized training.
|
| The purpose probably is keeping human speech understandable
| through the often noise-filled channel of ambient sound.
| Human speech with no redundancy would have a hard time
| fighting the noise floor.
| benjaminwootton wrote:
| The bigger issue is that LLMs haven't had much training on Q as
| there's little publically available code. I recently had to try
| and hack some together and LLMs couldn't string simple pieces of
| code together.
|
| It's a bizarre language.
| haolez wrote:
| I don't think that's the biggest problem. I think it's the
| tokenizer: it probably does a poor job with array languages.
| quotemstr wrote:
| Perhaps for array languages LLMs would do a better job
| running on a q/APL parse tree (produced using tree-sitter?)
| with the output compressed into the traditional array-
| language line noise just before display, outside the agentic
| workflow.
| Analemma_ wrote:
| This is the dream, but it keeps crashing and sinking
| against reality. It seems intuitive that running language
| models on the AST should work better than running them on
| the source code, but as far as I'm aware every attempt to
| do this has resulted in much worse performance. There's so
| much more training data available as source code, and
| working in source code form gives you access to so much
| more outside context (comments, documentation, Stack
| Overflow posts), that it more than cancels out the
| disadvantages.
| gabiteodoru wrote:
| Perhaps if we also trained them on natural language ASTs
| at the same time when asking the questions? :)
| neprotivo wrote:
| This approach of solving a problem by building a low-perplexity
| path towards the solution reminds me of Grothendieck's approach
| towards solving complex mathematical problems - you gradually
| build a theory which eventually makes the problem obvious.
|
| https://ncatlab.org/nlab/show/The+Rising+Sea
| nextaccountic wrote:
| > you gradually build a theory which eventually makes the
| problem obvious.
|
| Which incidentally is how programming in Haskell feels like
| bonesss wrote:
| " _A good engineer will solve your problem in 5 days. A great
| engineer will spend 3 days figuring out how to solve it in
| 2._ "
| orbifold wrote:
| what is striking to me is how far reasoning by analogy and
| generalization can get you. some of the deepest theorems are
| about relating disparate things by analogy.
| Veedrac wrote:
| > Let's start with an example: (2#x)#1,x#0 is code from the
| official q phrasebook for constructing an x-by-x identity matrix.
|
| Is this... just to be clever? Why not (!x)=/:!x
|
| aka. the identity matrix is defined as having ones on the
| diagonal? Bonus points AI will understand the code better.
| quotemstr wrote:
| The vibe I get from q/kdb in general is that its concision has
| passed the point of increasing clarity through brevity and is
| now about some kind of weird hazing or machismo thing. I've
| never seen even numpy's verbosity be an actual impediment to
| understanding an algorithm, so we're left speculation about
| social and psychological explanations for why someone would
| write (2#x)#1,x#0 and think it beautiful.
|
| _Some_ brief notations make sense. Consider, say, einsum:
| "ij->ji" elegantly expresses a whole operation _in a way that
| exposes the underlying symmetry of the domain_. I don 't think
| q's line noise style (or APL for that matter) is similarly
| exposing any deeper structure.
| cwmoore wrote:
| It's...remarkable. Is this meant to be entered on a payphone?
| I will never be cool enough.
| dzaima wrote:
| Unless the interpreter is capable of pattern-recognizing that
| whole pattern, that will be less efficient, e.g. having to work
| with 16-bit integers for x in the range 128..32767, whereas the
| direct version can construct the array directly (i.e. one byte
| or bit per element depending on whether kdb has bit booleans).
| Can't provide timings for kdb for licensing reasons, but here's
| Dyalog APL and CBQN doing the same thing, showing the fast
| version at 3.7x and 10.5x faster respectively:
| https://dzaima.github.io/paste/#0U1bmUlaOVncM8FGP5VIAg0e9cxV...
| sannysanoff wrote:
| while both versions are O(N^2), your version is slower because
| comparison operation, which affects execution speed. This is
| suboptimal. q)x:1000 q)\t:1000 sum (til
| x)=/:(til x) 889 q)\t:1000 sum (til x)=/:(til x)
| 871 q)\t:1000 sum (2#x)#1,x#0 602 q)\t:1000
| sum (2#x)#1,x#0 599
|
| upd: in ngn/k, situation is opposite ;-o
| chmod775 wrote:
| LLMs were created to use the same interface as humans
| (language/code).
|
| Asking humans to change for the sake of LLMs is an utterly
| indefensible position. If humans want terse code, your LLM better
| cope or go home.
| afc wrote:
| Disagree. If some small adjustments to your workflow or
| expectations enable you to use LLMs to produce good, working,
| high-quality code much faster than you could otherwise, at some
| point you should absolutely welcome this, not stubbornly refuse
| change.
| chmod775 wrote:
| Somehow I don't think writing verbose English to communicate
| with an LLM is ever going to beat a language purpose-built
| for its particular niche. Being terse _is the point_ and what
| makes it so useful. If people wanted to use python with their
| LLM instead, they have that option.
| geocar wrote:
| I think there's a mighty big assumption in there.
|
| I see no reason to believe LLMs can write working let alone
| good or high-quality code, nor that the adjustments to my
| workflow or expectations will be small. But sure, if such a
| thing happened, I would probably welcome it.
|
| Meanwhile, there are _people_ who write good and high-quality
| working code faster than me, and they _all_ write as much as
| possible on one line with the most bare-bones of text
| editors, so I will continue to learn from them, rather than
| the people who say LLMs are helping _them_. Maybe you should
| reconsider.
| mikkupikku wrote:
| Do you swing a nailgun?
|
| Use the tool according to how it works, not according to how
| you think it should work.
| chmod775 wrote:
| Chances are hell is going to freeze over before people start
| writing verbose q code. Q being less verbose than
| alternatives is the whole point. Nobody is feeling any
| pressure to bend over backwards to accommodate the guy who
| struggles to get by when his LLM can't explain a piece of
| code to him.
|
| To use your nailgun analogy as an example: Waddling in with
| your LLM and demanding the q community change is like walking
| into a clockmaker's workshop with your nailgun and demand
| they accommodate your chosen tool.
|
| "But I can't fit my nailgun into these tiny spaces you're
| making, you should build larger clocks with plenty of space
| for my nailgun to get a good angle!"
|
| No, we're not going to build larger clocks, but you're free
| to come back with a tiny automatic screwdriver instead.
| Alternatively you and your nailgun might feel more at home
| with the construction company across the street.
| mikkupikku wrote:
| I'm pretty sure the time will soon come when nobody is
| trying to accommodate the personal tastes and preferences
| of developers anymore; languages and tools will be chosen
| based on how well LLMs work with them, and the way the LLMs
| are used with those will be determined again by the traits
| of the tool, not the preferences of the user. Management
| won't be in the mood to humor devs who are stuck in their
| old mindset of writing code themselves.
|
| I could be wrong. Time will tell.
| Majromax wrote:
| > When I(short proof)=I(long proof), per-token average surprisal
| must be lower for the long proof than for the short proof. But
| since surprisal for a single token is simply -log P, that would
| mean that, on average, the shorter proof is made out of less
| probable tokens.
|
| This assertion is intuitive, but it isn't true. Per-token entropy
| of the long proof can be larger if the long proof is not minimal.
|
| For example, consider the "proof" of "list the natural numbers
| from 1 to 3, newline-delimited." The 'short proof' is:
|
| "1\n2\n3\n" (Newlines escaped because of HN formatting)
|
| Now, consider the alternative instruction to give a "long proof",
| "list the natural numbers from 1 to 3, newline-delimited using #
| for comments. Think carefully, and be verbose." Trying this just
| now with Gemini 2.5-pro (Google AI Studio) gives me:
|
| "# This is the first and smallest natural number in the requested
| sequence.\n 1
|
| # Following the first, this is the second natural number,
| representing the concept of a pair.\n 2
|
| # This is the third natural number, concluding the specified
| range from 1 to 3.\n 3"
|
| We don't have access to Gemini's per-token logits, but repeating
| the prompt gives different comments so we can conclude that there
| is 'information' in the irrelevant commentary.
|
| The author's point regains its truth, however, if we consider the
| _space of all possible long proofs_. The trivial 'long' proof
| has higher perplexity than the short proof, but that's because
| there are so many more possible long proofs of approximately
| equal value. The shortest possible proof is a sharp minimum, but
| and longer proofs are shallower and 'easier'.
|
| The author also misses a trick with:
|
| > Prompted with "Respond only with code: How do you increment i
| by 1 in Python?", I compared the two valid outputs: i += 1 has a
| perplexity of approximately 38.68, while i = i + 1 has a
| perplexity of approximately 10.88.
|
| ... in that they ignore the equally-valid 'i = 1 + i'.
| gabiteodoru wrote:
| Thanks so much for this challenge! I just ran the experiment
| with i = 1 + i and you're absolutely right - it breaks my
| theoretical framework (same semantic information, but much
| higher perplexity).
|
| While setting this up, I realized I hadn't used chat templates
| in my original measurements (rookie mistake with an Instruct
| model!). Re-running with proper methodology completely flips
| the results - the terse version actually wins.
|
| I'll add a correction note to the article once AWS/Medium comes
| back online, and will write a proper follow-up with all the
| corrected experiments. Your comment literally made the research
| better - thank you!
| segmondy wrote:
| First pass on my local deepseekv3.1-Terminus at Q4 answered it
| correctly. if anything, i think LLMs should write terse code,
| Q/J/APL/Forth/Prolog/Lisp, tokens is precious. It's insane to
| waste precious tokens generating Java, javascript and other
| overly verbose code...
|
| https://pastebin.com/VVT74Rp9
| gabiteodoru wrote:
| It did go back on itself 3 times, no? "Actually, let's trace
| for x=3:" (it had just computed for x=3 the first time); then
| "Better to check actual q output:" -- did it actually run it in
| a q session, or just pretended? And another one "That doesn't
| seem to align. Let's do it step by step:"
| socketcluster wrote:
| I was kind of taken aback by the author's definition of 'terse'.
| I was expecting a discussion about architecture not about syntax
| aesthetics.
|
| Personally I don't like short variable names, short function
| names or overly fancy syntactical shortcuts... But I'm obsessed
| with minimizing the amount of logic.
|
| I want my codebases to be as minimalist as possible. When I'm
| coding, I'm discovering the correct lines, not inventing them.
|
| This is why I like using Claude Code on my personal projects.
| When Claude sees my code, it unlocks a part of its exclusive,
| ultra-elite, zero-bs code training set. Few can tap into this
| elite set. Your codebase is the key which can unlock ASI-like
| performance from your LLMs.
|
| My friend was telling me about all the prompt engineering tricks
| he knows... And in a typical midwit meme moment; I told him,
| dude, relax, my codebase basically writes itself now. The coding
| experience is almost bug free. It just works first time.
|
| I told my friend I'd consider letting him code on my codebase if
| he uses an LLM... And he took me up on the offer... I merged his
| first major PR directly without comment. It seems even his
| mediocre Co-pilot was capable of getting his PR to the standard.
| ahussain wrote:
| I'd bet a lot of people are trying to optimize their codebases
| for LLMs. I'd be interested to see some examples of your ASI-
| unlocking codebase in action!
| gabiteodoru wrote:
| Author here. Majromax challenged me to test `i = 1 + i`, which
| broke my theoretical framework. While setting up that experiment,
| I realized I hadn't used chat templates in my original
| measurements (rookie mistake with an Instruct model!).
|
| Re-running with proper methodology completely flips the results -
| the terse version actually wins. I'll add a correction note to
| the article once AWS/Medium comes back online and will write a
| follow-up with the corrected experiments.
|
| This is open science working as intended - community scrutiny
| improves the work. Thank you all for the engagement, and
| especially to Majromax for the challenge that led to discovering
| this!
| daxfohl wrote:
| I was wondering recently, is fine tuning an effective way to make
| this the default? If so, does fine tuning this behavior on one
| language have a carry-over effect on other languages (maybe even
| non-programming language?), or is the effect localized to the
| language of the fine-tuning dataset?
| bArray wrote:
| > Medium will be back.
|
| > Due to a global hosting outage, Medium is currently
| unavailable. We're working to get you reading and writing again
| soon.
|
| > -- The Medium Team
|
| Dang.
___________________________________________________________________
(page generated 2025-10-20 23:01 UTC)