“Instead, we recommend to use C arrays”
I actually agree with the quote — when I started out using Racket, I wrote all my data structures using cons
, car
and cdr
and this quickly became unmanageable. ActivityLog2 is still a Racket application today because I could use boring things such as structs and objects, there are still a few places where I use S-Expressions to hold data and those parts of the code base are the messiest and most fragile. My two cents.
scheme teachers have been telling us since “forever” to not do that (produce this cons-car-cdr salad)
inbetween we invented json and do the same thing just with dicts thrown in :shrug:
I had the same experience as @alexharsanyi in school. Our professor taught us to use nested lists for everything and never bothered to teach us about creating interface functions, let alone using records. Most my classmates came out of the intro courses thinking that Scheme was an unwieldy mess and C was a comparative joy to use.
I am starting to understand why my colleague, while a LISP fan, is writing code with lists of lists everywhere. It’s a mess.
(when learning common lisp in my undergrad my tutors also were not helpful by (trying to) make Java look superior. but that’s another story…)
I think that most the classic lisp books I’ve read do things that way until very late in the text. If they do introduce better ways of doing things, they don’t generally do a good job of motivating why you would want to use these other constructs.
My guess is it’s because the idea that you can use one simple structure - a cons cell - to build just about anything is more academically interesting. Or perhaps was in the 80s and 90s. Theoreticians tend to have an affinity for golden hammers.
:laughing:
I’ll point out that many of the other languages are taking toe-in-the-water in the other direction. Usage of unstructured tuples and multiple returns is actually becoming more common in languages like c# and javascript and holding steadyish in python (though both namedtuples and dataclasses are great)
rather concerning trend
I also see a lot of resistance to that trend. I guess I can’t speak for JavaScript, but, at my last job, using tuples was all but banned.
In Python, the general consensus among people who worry about these things seems to be that, now that we have namedtuple
, using regular tuples is only permissible in legacy code where you can’t change it without breaking too many consumers.
(I’m not sure how well people who don’t worry about these things realize that, since the largest block of legacy code that uses unnamed tuples is the standard library.)
so to be clear, I think mostly its fine. In js for example - while there are some uses I arch my eyebrow at, the big one is standard functions like Object.entries()
where it is pretty reasonable - labels wouldn’t really add any value to that. In C#, the implementation of value tuples is super weird and almost can’t be compared (there are unstructured tuples, but you can annotate a method so that the compiler is able to convert labels to indicies for you meaning no labels at run time). In python…I mean, yes, it can be bad, but also is largely used for stuff like dictionary iteration which again, you’re not really adding a ton of semantic usefullness with labels there
I find lists handy. Sometimes, in cases where it’s very unlikely they will change, I’ll just use them directly. In other cases, I’ll hide them behind an interface, so the impact of a change is minimal.
It’s also important to call out the difference between “using a simple data structure to hold random stuff while prototyping and I don’t yet know entirely what I’ll need” and knowing the problem quite well and having the ability to go back and remove complexity by building structs, classes, or whatever is best.
Every time I’ve ever started by defining my data types, class hierarchies, etc. up-front, I’ve inevitably been wrong. And wrong in ways that cost me more time than if I had just started solving the problem first and then went back once I learned more about what I was actually solving instead of what I thought I was solving. :wink:
I’d caution this with how quickly a “prototype” turns into something “in production”, at which point you really regret not putting the effort in naming tihngs at least :shrug:
also try reading someone else’s code that only uses an amorphous list of stuff
Well, and the thing I was criticizing was not the use of lists as a universal data structure. It’s the failure to teach the practical bits around how to use them in code that’s meant to last.
If I remember right, Realm of Racket hits this very early on, while SICP and The Little Schemer don’t really touch on it very effectively.
Which is fine for what they are - they’re books that are more for computer scientists than working programmers - but, at the same time, I’m pretty happy to forgive people for reading a book that people widely tout as the be-all-end-all of lisp, and assuming that it’s the be-all-end-all of lisp.
Agreed. But - like everything in life - it’s a cautionary tale of choosing any extreme.
@andreas.hofmeister has joined the channel
This discussion is really about types, and there are two ways that a language can treat types. (1) A type can mean a predicate on values in the language (possibly based on “type tags”). For example, in Racket there are strings and there are numbers and you can tell them apart with the string?
and number?
predicates, and you can talk about the “types” String
and Number
as meaning those sets of values. (2) A type can mean a distinct interpretation that you impose on some data. For example, the C double
and intptr_t
and int*
types share all of the same “values” on my desktop (words of 64 bits), but the type controls what operations are available and in some cases how the operation is interpreted (eg, floating-point addition vs integer addition vs pointer addition). More generally, the meaning of a type can involve some combination of those two ideas. For example, the Java type ArrayList<String>
is part predicate and part interpretation (the ArrayList
class “tag” exists at run time; the String
type parameter does not). I’ve been thinking about this because in ordinary Racket programming, we usually use struct
to make new types-as-distinct-sets-of-values, but reasoning about macros requires thinking about the interpretations that macros impose on syntax that is just jumbles of atoms and pairs.
The discussion really was about syntax and not data structures.
It’s hard to tell from the quote because this person seems to be confusing syntax for data. (this is feature not a bug lol)
Also it is true that once you start having lists of lists it becomes more difficult to manage the structures complexity naively and most people would use fmap or match in that case. But, that’s a much more advanced topic for dealing with list structured data that this person probably wasn’t aware of or considering.
It’s quite a thing to assert what this discussion was really about :shrug:
Okay, fair, it’s not all the conversation is about. I tried to find a better opening line but I timed out before I found something I was happy with. Maybe just “Here is a generalization of one issue that I find interesting.”
“Nesting lists for everything” has an advantage over record
or struct
is that you can easily traverse mixed data structures. This is very useful for prototyping IMO. AFAIK Lisp or Racket’s struct
has no feature something like auto deriving Functor
, Foldable
or Traversable
.