I love Lisp (particularly Scheme), and heard that Janet was strongly inspired by Lisp and is "really Lisp underneath", but is really fast and strong at math, so I thought I'd give it a try, and after learning it I started wondering to myself, "why am I not simply using Lisp?"
Though arguably lispy, Janet just wasn't lispy enough for me. It was missing the simple, elegant sexp syntax I dearly loved, and I started to wonder what huge win I was getting from using it instead of just using Lisp or Scheme? Having not found a good answer, I did just that.
That was the last time I bothered trying to learn a "Lisp-like" language that wasn't actually a Lisp, and decided to just stick to Lisp/Scheme. They do everything I need, are good at math, and are plenty fast enough for me.
>It was missing the simple, elegant sexp syntax I dearly love
In what sense does Janet not have sexp syntax? Seems plenty sexpy to me. Purists seem to say it's not a lisp because its underlying data structure is not (cons-based) lists as in classical lisp, but I don't see what syntactic difference there is.
Oops.. I think you're right.. It was Julia, not Janet...
now I feel like an idiot, and apologize for my uncalled-for, unfair, and undeserved mischaracterization of Janet, a language I never tried.
I wish I could take it back, but HN won't let me delete my post. I apologize. Mea culpa.
Lisp usually has a lot of non-list data structures like arrays. records, strings, classes/objects, hashtables, ... For some data structures there is built-in syntax and with an extensible reader (the extensible parser for s-expressions) the user can add additional syntax. Janet uses a non-extensible parser for the data syntax.
What Janet makes a 'not really a Lisp' is that "LISP" stands for "List Processor". Janet isn't exactly that, since it is not using linked lists as a core data structure - unlike Lisp where its List Processing features are built on top of linked lists made of cons cells.
(1 2 3) is called a "tuple" in Janet and represents something like an immutable array.
Lisp especially is built upon linked lists, which have different costs for (more or less) primitive operations (add to the front, get the front item, get a rest list, get a random element, add to the end, ...) compare to a primitive vector. There are also other features not easily replicated by vectors.
CL-USER 40 > (rest '(1 2 3))
(2 3)
Above REST operation does not allocate any memory.
CL-USER 41 > (subseq '#(1 2 3) 1)
#(2 3)
Above SUBSEQ returns a new vector. Alternatively it would need a more clever implementation underneath.
OTOH getting a random element has a different complexity in a linked list vs. a vector.
…anything that requires hashing as a fundamental part of the guarantee. Which happens to be quite a lot of the structures most of us use every day. Sets, maps, etc.
They’re often called “hash sets” or “hash maps” in other languages - they are called this for a reason, and certainly not because they could be implemented as a list.
std::map is not a good example anyway, you want to consider std::unordered_map for a more appropriate comparison. C++ is weird that way. (What C++ calls a map is not what most languages call a map. std::map doesn’t even satisfy O(1). You’d be surprised how many working C++ developers don’t even realize the unnecessary performance cost they take when they decide to use std::map, because it’s not a proper hash map. )
But this thread is not about the finer differences in implementation of maps but rather whether or not they are basically just lists. They are not.
Agree broadly that this thread has gotten too long off what was basically a joke, but!
Please measure before you make changes to your maps for perf reasons. Yeah this forum all knows their big-O, but B-tree maps like std::map often perform better than hash maps on real-world architectures.
You said "requires" hashing. Sets and Maps do not require hashing. Through it is correct to observe the unfortunate naming convention in the c++ std lib.
std::set and std::map should be std::ordered_set and std::ordered_map
sts::unordered_set and std::unordered_map should be std::hash_set and std::hash_map
If this were so then it might make the incorrect usage of these two options less prevalent. But they are both map and set structures just each version has other guarantees that in some scenarios may be more or less useful.
You interpreted my reply as “all sets and maps require hashing“ instead of “sets and maps are examples of data structures that can require hashing”… which they are.
Imagine someone says, every fruit in the world is sour, and someone answers, no there are plenty of fruits that are sweet, such as apples, and then an entire thread gets launched in an irrelevant direction pointing out that actually some apples are sour, which has no bearing on the original point.
Sure, my thinking is that a cons cell can build singly linked lists and node-based binary trees. Some data structures are based only on those, but most involve an array of some kind. In scheme, for example, it's the combination of cons (i.e. lists and trees) and vector (i.e. arrays) that allows for arbitrary data structures. It's very constraining to have only the lists.
- array: not a singly-linked list.
- hash table: often an array of singly-linked lists, so not a list.
- red-black or AVL tree: can be built with cons cells.
- doubly-linked list: not a singly-linked list
- double-ended queue: array of double-ended queues, so not a list. Could also be implemented as a doubly-linked list.
So what about streams? Functions? Closures? Call/cc structures?
I understand your point if you are speaking in literal terms about just simple cons cells, but in practical Lisp/Scheme code, you don’t really rely on just the basics to do things.
I think there’s a hyperfocus sometimes on the simplicity of the core of lisp, the apply/eval balance, but it’s quite possible and often easy and convenient to perform normal programming tasks with these languages as well.
> I understand your point if you are speaking in literal terms about just simple cons cells, but in practical Lisp/Scheme code, you don’t really rely on just the basics to do things.
Agreed.
> So what about streams? Functions? Closures? Call/cc structures?
Those are interesting examples. They are all data structures in a sense (especially streams and closures), but to me they are more like functions than data (yes, yes, functions are values, blah, blah). Call/cc is a reification of execution control; thinking of it in terms of data stretches my brain.
That's a ridiculous statement. Linked lists have real performance benefits in some applications. A good "modern CS toolbox" includes the ability to make the right choice. Which, if you believe they are fundamentally useless, clearly you lack.
In many cases you can get around these issues by being a little more clever in how you allocate nodes. In some cases you don't have the luxury of allocating all elements next to eachother anyway, in which case an intrusive linked list is often the best option to minimise copying. You might say use a vector of pointers or a circular buffer, but if you're in a timing sensitive context you might be unable to realloc.
Hell, memory allocators themselves are often implemented using some form of linked list. You tend to see them quite a bit at very low levels like in kernels.
The list of features available out if the box is pretty impressive, especially if you are scripting something concurrent. I would tolerate the syntax quirks in exchange for that.
Or maybe your particular Scheme has all of that out of the box, too. Which one do you normally use, if you don't mind?
Though arguably lispy, Janet just wasn't lispy enough for me. It was missing the simple, elegant sexp syntax I dearly loved, and I started to wonder what huge win I was getting from using it instead of just using Lisp or Scheme? Having not found a good answer, I did just that.
That was the last time I bothered trying to learn a "Lisp-like" language that wasn't actually a Lisp, and decided to just stick to Lisp/Scheme. They do everything I need, are good at math, and are plenty fast enough for me.