lainchan / λ

Code Readability in PLT

Cat Mother · 2021-07-26 23:43
I'm a fool. Anything more complicated than a toy project makes my eyes glaze over. What interesting approaches are there (from a PLT perspective) to improving readability? I'm curious of some more esoteric ideas that lain might've heard of since structured programming, modules, DSLs, pure FP, etc. are all old hat by now. One aspect of readability in particular that has been on my mind is how debug statements have the same semantic weight as the core algorithm. Worse still, program flow or type signatures need to be changed to permit some of the more complex forms like error handling, statistics etc. More abstractly, there's a lot of cases where it's impossible to cleanly separate concerns because the only efficient implementation requires intertwining the logic. Have there been any attempts at overcoming this? It's hard to think of something general that wouldn't be fragile or narrow like stream fusion.
Dujour · 2021-07-28 01:15
>>26886 >I'm a fool. Don't be so harsh. Many programmers are foolish, usually those who are most successful. >Anything more complicated than a toy project makes my eyes glaze over. What's wrong with that? I certainly don't want to, and can't, read a program many millions of lines long; even thousands of lines is getting too long. It's an idiot who believes playing king of the hill on a large program makes him wise. >What interesting approaches are there (from a PLT perspective) to improving readability? Strict modularity is a good one; notice many popular languages don't allow for it. >I'm curious of some more esoteric ideas that lain might've heard of since structured programming, modules, DSLs, pure FP, etc. are all old hat by now. The only idea that actually works is keeping the entire program simple. Notice that pulling in dozens of dependencies immediately destroys this. The programs in TAoCP are small, well-documented, and correct. >One aspect of readability in particular that has been on my mind is how debug statements have the same semantic weight as the core algorithm. That's a sign of a poor environment. Use a debugger and real debugging methods. >More abstractly, there's a lot of cases where it's impossible to cleanly separate concerns because the only efficient implementation requires intertwining the logic. Unsurprisingly, this is something the automating device called a computer should automate. Use of a decent and suitably abstract language makes this effortless. >Have there been any attempts at overcoming this? Yes, at the dawn of computing and following decades, but since computers are built for markets, they've instead been designed to trivially fail and be almost impossible to use. What hasn't has been taken by idiots who believe using broken machines makes them cool, so they also exacerbate it. Here's a book I'm going to be reading soon. I've already read the linked appendix: Softword: Provenance for the Word 'Software' http://niquette.com/books/softword/tocsoft.html Software Does Not Fail http://niquette.com/paul/issue/softwr02.htm >One handy software engineering tool is the 'debugger,' which is a misnomer tantamount to calling a 'magnifying glass' an 'insecticide.'
Frank Garza · 2021-08-25 12:38
>>26886 I'm working on a PL that accidentally became readable as a result of design decisions not focused on readability. One of the gripes I have with most mainstream PLs is that codebases do not evolve nicely. Open a diff between two not-so-distant revisions, and you'll see a sea of red and green, even though only a handful of meaningful changes were made. E.g. a simple String fun doTheThing(someValue) { result = sendRequest(someValue) return result } doTheThing("hello, function!") turns into a monstrosity like class ThingDoer { constructor([Inject] requestSender) async Maybe<String> no-longer-fun doTheThing( someValue, url ) { result = nothing requestConfig = { url } try { result = await requestSender.sendRequest(requestConfig, someValue) } catch { } return result } } await ThingDoer(sender).doTheThing("http://wherever", "oh god why") If you have a sharp eye, you'll see that changes are trivial and the procedure stays mostly the same. But the code was changed completely, and the diff will not even give you a hint on how similar it is. My language mostly is devoid of this issue, as: - every function assumes that its dependencies (i.e. everything that wasn't introduced in its scope) could be provided by the caller, eliminating the need for DI-aware classes; - every call is awaiting (unless specified otherwise), as I hardly ever want to fire-and-forget a request; - error handling goes after the function body, as most of the time I only care about the happy path; - parameters, return types, helper variables and other soykaf goes after the body as well, since a computer doesn't care where they are defined, git diff becomes smaller, and a human is able to guess that a not-previously-assigned variable comes from parameters or is derived from them. As a result, I got fun doTheThing result = sendRequest(someValue) return result [parameters] someValue doTheThing("hello, function!") turning into fun doTheThing result = sendRequest(config, someValue) return result [parameters] someValue url [bindings] config = { url } doTheThing("hello, function!") [with] httpSendRequest as sendRequest Don't mind this exact syntax, the public release will look somewhat different. But I believe that what I'm working on will make some ripples, and we'll get to see a much more readable future across the entire industry.
The Phantom Phreak · 2021-08-25 18:49
>>27243 To me that fattened example just looks like it needs a wrapper function (or macro).
John Titor · 2021-11-12 22:44
For general readability, if a piece of code does a well defined "thing", I like to put it in its own function. So for example, if you have a check to see if an array contains a value, it makes sense to move it to a function. It reduces boilerplate and makes the overall logic of your code easier to read.
Ivan Horvat · 2021-11-13 05:10
>>28530 Better definition of what it means for a piece of code to do a "thing": if you can accurately explain what the importance of that section of code is in the context of the surrounding program, then it deserves to be in a function. For instance " test if some value is bigger than another" doesn't need to be its own function but "merge two sorted lists" does.
The Fin · 2021-11-13 19:02
>>28534 Thanks. I was at work when I wrote >>28530 and didn't have much time to think of a good example.
Miho Iwakura · 2022-04-15 06:26
>>28530 I agree but my criterion is probably a bit different: if the result allows the program to read more like an English sentence, it's good; which is why name choosing is a very important (and often difficult) programmer job.
Otto Normalverbraucher · 2022-04-15 17:10
anyone known why let is also similar to var
Apoc · 2022-09-05 09:50
>>27243 It's been just over a year. Are you still with us, Lain? Did you ever publish this language? It looks really cool.
J.C. Denton · 2022-09-06 06:24
I should have expanded replies on this thread a long time ago. >>26893 >What interesting approaches are there (from a PLT perspective) to improving readability? >Strict modularity is a good one; notice many popular languages don't allow for it. >I'm curious of some more esoteric ideas that lain might've heard of since structured programming, modules, DSLs, pure FP, etc. are all old hat by now. >The only idea that actually works is keeping the entire program simple. The tragic irony of this story being that, to my understanding, the forerunner to true modular programming, file line (size) limits to encourage code management, when picked up by management and taken past the extreme, percipitated Dijkstra's famous structured programming paper by making an excess of gotos understandably necessary, and so just about gave us all our modern paradigm tomes of promises that haven't panned out. OO wasn't even the first time we made that mistake, sadly. And now functional, like structured, is making things better but still not living up to the technical hype. Understandably. Like was said, nothing beats just keeping everything simple.
The Phantom Phreak · 2022-10-10 22:28
>>27243 IBM-Kettler Courier lowercase l (ell) is 20th Century. https://www.slant.co/topics/67/~best-programming-fonts
Erik Johansson · 2022-10-13 10:04
>>32670 Life's getting in the way, so no, it is not published yet. However, even at a pace of 1 commit per month I'm making progress on both the language and it's development environment (I went nuts on the idea of frame-based editing). Currently I have a [web editor + nodejs server] which can rebuild and reload itself within a second — works fine for me. Don't keep your hopes up to see it live this year.
stack · 2022-10-29 09:05
Readability comes from directness and simplicity. Directness is about how well the code the programmer writes corresponds to what the code (semantically) does, whereas simplicity is about how many things the programmer needs to remember in order to read someone else's code. Most languages have one but not the other. C is simple, but not very direct. It's easy to memorize its entire feature set, but it's hard to make those features do the exact thing you want them to without your code getting messy and verbose. Inform 7, on the other hand, is direct but not simple. It's easier to discern what a given block of Inform 7 code does in the broad strokes, but the specific meaning of each line and what each of the words means is a bit murkier, because the rules are less consistant.
LispOverlord · 2022-11-21 01:00
>>26886 > One aspect of readability in particular that has been on my mind is how debug statements have the same semantic weight as the core algorithm. Worse still, program flow or type signatures need to be changed to permit some of the more complex forms like error handling, statistics etc. More abstractly, there's a lot of cases where it's impossible to cleanly separate concerns because the only efficient implementation requires intertwining the logic. Yes, there is a whole field of programming language theory that addresses this exact problem, and it’s called Aspect Oriented Programming, and it’s based on dynamic-extent functions: https://static.aminer.org/pdf/PDF/001/067/237/dynamically_scoped_functions_as_the_essence_of_aop.pdf
Nemissa · 2023-02-24 03:41
>>33016 https://minhaskamal.github.io/DownGit
AXS · 2023-02-24 08:22
>>26886 >One aspect of readability in particular that has been on my mind is how debug statements have the same semantic weight as the core algorithm. Try to use proper literate programming with tests written in cucumber (in a literate way). Is not that hard to read code from bigger systems, once you have the basic concepts. Take kubernetes as an example, read the Tanenbaum book on distributed systems, have a good understanding of linux, networking, containers and storage, can you can read the code quite easily. The actual problem is when a system should be simple, and is written in a way that you can't wrap your head around it. Then clean code and clean architecture come to save it. And basically all corporate software fall into the category of "should be simple", because they usually solve simple problems. Literate programming shines specially in the helms of science, where things are light in code, but concept heavy. https://jonathanabennett.github.io/blog/2019/05/29/writing-academic-papers-with-org-mode/
J.C. Denton · 2023-02-24 10:38
Consider learning how to read the part of programs that's significant to the program's behaviour, rather than those parts which are not. As for Knuth, his ideas on literate programming are halfway on the right track in that programs should not be separated from their commentary (in contrast to things like Doxygen). On the other hand they're antiquated because source code is rigorous enough for a compiler to translate into an executable program, and therefore also the best possible means of communicating the program's literal meaning. As such commentary can at best provide context. >>33201 This perspective ignores that any language that supports structured programming (loops, if/then/else, subroutines, and recursion) already provides every tool necessary to construct the abstractions for whatever level of concision is desired. In the face of abstractions such as high-level programming languages (as opposed to e.g. Forth) the idea of "simplicity" is disingenous and ignorant.