8 ms·
NIL
- dreamcompiler 5y agoNIL is certainly an overly-overloaded concept in Common Lisp. Scheme did a better job by separating the boolean type from the empty list but in practice I don't find NIL much of a hindrance in CL.
- jinwoo68 5y agoHuh? In my opinion it’s Scheme’s one million dollar mistake. Common Lisp’s identification of NIL and the empty list makes things very ergonomic in so many cases that I wonder if it’s by design or we were just lucky.
- spfzero 5y agoNIL equating to the empty list is fine. The empty list equating to: didn’t find what you wanted, but here’s an empty list instead? Hope you weren’t looking for an empty list though, then you’d be really confused, sorry. Nah, I’ll take “false” thanks.
- kazinator 5y ago> Nah, I’ll take “false” thanks. false is an object which can be stored in the container, and which you could be looking for. You've just migrated the problem away from empty lists.
- spfzero 5y agoThat's right, then I'd just have a mirror of the original problem. I could store empty lists to represent false, but that's not really any better.
- deleted 5y ago[deleted]
- JasonFruit 5y agoI've been writing a bunch of Common Lisp recently, where I usually have been more interested in Schemes. I'm less bothered by its little inconsistencies, like the empty list not needing to be quoted, than I used to be. Maybe because I'm getting older, and people develop cruft just like programming languages. I'm not really small and elegant anymore, so why should my Lisp be?
- moonchild 5y ago> I'm less bothered by its little inconsistencies, like the empty list not needing to be quoted, than I used to be CL certainly has its warts, but that is not one of them. Most data types other than pairs and symbols are self-evaluating. You don't quote number literals, for instance! That the list terminator is not self-evaluating is definitely a scheme wart. (Sadly, I'm told that motions to change this behaviour in r7rs-large were rejected.)
- JasonFruit 5y ago'(1 2 3) '(1 2) '(1) () ; wart
- moonchild 5y agoThat's syntax sugar and it's lying to you for convenience's sake. (+ . (1 . (2 . (3 . ())))) #| --> 6 |# (quote . (1 . (2 . (3 . ())))) #| --> (1 2 3) |# (1 2 3) is a pair and we have a rule for evaluating pairs: unless the car is a symbol denoting a special form (1 is not one such), apply the car to the cdr. So we need to quote it if we want to get it as a literal. () is a special list terminator object; it is not a pair—as this dotted list syntax shows—and it has neither car nor cdr, so our previous rule for evaluation does not apply. However we have another conventional rule: objects which are not pairs and are not symbols should evaluate to themselves. This applies to strings, characters, numbers, vectors... Why not to the list terminator?
- JasonFruit 5y ago
- tosh 5y agorelated article on nil punning: https://lispcast.com/nil-punning/ https://lispcast.com/nil-punning/
- bitwize 5y agoI'm reminded of the fact that in Smalltalk, nil accepts any message, does nothing, and returns nil. The same is true of Objective-C's object semantics, which means that language has two flavors of null pointer, one of which will scream SIGSEGV if you do anything significant with it, the other having more subtle failure modes...
- fmakunbound 5y agoYou actually get message not understood and dropped into the Smalltalk debugger if you send a message to nil.
- massung 5y agoJust to be clear for @bitwize, nil is an object like everything else in smalltalk. It's an instance of UndefinedObject. It isn't special in any way and executes any method it understands just like all other objects. Object>>isNil ^false UndefinedObject>>isNil ^true It drops into the debugger if you pass it a message it doesn't understand.
- bitwize 5y agoI think I might've described how Objective-C works. It's been a while since I worked in depth with Smalltalk.
- codr7 5y agoIt's oh so convenient until it isn't; until you need to make distinctions between missing values and booleans. I still haven't decided if it's a good idea or not, and I've been hacking Common Lisp for more than 20 years.
- blacktriangle 5y agoIn which data structures? For example in CL hashes use multiple returns so in 99% of cases you can just not care, and when you do care about the difference you just check the second return value.
- fiddlerwoaroof 5y agoYes, I think the two return values convention in CL mostly makes this a non-issue.
- codr7 5y agoI've run into issues when interfacing databases and writing interpreters, and probably more that I've forgotten. They're just minor culture clashes that add up...
- ruricolist 5y agoI like the way CL does things but it makes dealing with JSON occasionally annoying.
- fmakunbound 5y agoDo you mean, for example, a null node versus a missing node?
- ruricolist 5y agoI mean distinguishing between `null`, `false`, and `[]` on the JSON side. Most JSON libraries for CL by default parse them all as `nil`. (ST-JSON excepted.)
- brundolf 5y agoOne place this is really problematic is serializing structures to JSON. I worked on a JS front-end at a company whose back-end was all Lisp, and I cannot count the number times the app broke with "TypeError: Cannot read property 'map' of null", because every single list that would normally serialize to an array would serialize to `null` when it was empty unless the person writing the service went out of their way to do otherwise. At some point I wrote an API-validation utility almost entirely to help manage this problem.
- reikonomusha 5y agoAn alternative solution is don’t serialize from/deserialize to NIL. Use MY-JSON:TRUE, MY-JSON:FALSE, MY-JSON:NULL, etc. to create a perfectly bijective map. Lisp is a symbolic language after all, no reason not to take advantage of it.
- brundolf 5y agoThat would mean writing code for each endpoint to convert its response data into the JSON-explicit format instead of dumping it out directly; this wasn't a big company and we didn't have the man-hours for that extra layer I think when they manually fixed it in individual cases, they did something like `(or the-list json:empty-array)`. We just had to encounter that case in practice and then make a Jira ticket for them to go in and patch over it, and then do that over again each and every time it came up :P The utility I wrote[1] would match JSON responses against expected schemas and report the bad `null` (or otherwise), its location in the JSON, and which service it came from, in the JS console. It didn't stop errors from reaching production but it saved a lot of time debugging. [1] https://github.com/brundonsmith/simple-typechecker https://github.com/brundonsmith/simple-typechecker
- michaericalribo 5y agoI dig the way this explicitly defines the null content of `nil`. It makes intuitive sense, but it also makes me more conscious of the general principle of NULLity.
- reikonomusha 5y agoThis is what I consider to be a wart of Common Lisp: somewhat unsightly, not the most desirable thing to have, but rarely if ever impeding on your ability to do anything. Common Lisp as a language “knows” that NIL can be troublesome, so the rest of the language typically gives you facilities to avoid its pitfalls. Examples: - GETHASH returns two values: the element found (or nil), and whether it found the element or not. - GETF (which extracts a value from a property list) has a third optional argument for the value to return of the property isn’t found. It’s easy to make a unique sentinel value if you’re worried about collisions, for instance with an uninterned symbol: (let* ((not-found '#:not-found) (x (getf props 'key not-found))) (unless (eq x not-found) ;; do something only if KEY was found )) In this case, it’s not possible for PROPS to ever contain the NOT-FOUND value. NIL is one of the things Lisp had to compromise on to truly make it a “Common” Lisp.
- kazinator 5y agoThe issues with nil aren't return values from containers. That problem exists regardless of what object you return for representing "not found", since that object could be a legitimate value in the container. It is an inherent problem of in-band signaling, and the only fix is out-of-band signaling: multiple API calls, multiple values, wrapped values (e.g. returning a hash entry object containing a key and value), or alternative control flows (e.g. not-found exception). Now there is a real issues with nil being a symbol, Boolean false and empty list, and it is this: In a heterogeneous, recursively varying data structure, there are are possible ambiguities as to what nil represents: - is it a recursive element of the nested list structure: an empty sub-list? - is it just a symbolic element, so that we consider (a b nil t) to be a list of four symbols? - does it indicate false? In many cases, this problem does not exist for various possible reasons: - the data structure does not use multiple representations. Thus if nil is seen, it is known that it is a symbol, Boolean false or empty lists. - the data structure does use nil in multiple roles, but they are disambiguated by syntax: such as fixed positions in syntactic nodes, or other conventions. However, even if the problem is nonexistent in a given scenario, the data structure itself doesn't make it clear; there is still a possible readability problem for someone not familiar with the conventions. Someone staring at the data structure still has to guess about what is the meaning of a given nil, and refer to the documentation or, failing that, code. Furthermore, the system itself has a "printability problem". The Lisp printer has to decide whether to render nil as nil or as (). Sometimes you see suboptimal choices like the system emiting (LAMBDA NIL (FOO)) where you'd like to see (LAMBDA () (FOO)). This is the price we pay for the conveniences brought about by nil.
- DonHopkins 5y agoNIL: "New Implementation of LISP" http://www.ai.sri.com/~delacaze/alu-site/alu/table/Lisp-History.html http://www.ai.sri.com/~delacaze/alu-site/alu/table/Lisp-Hist... >The Macsyma group at MIT began a project during the late 1970's called the New Implementation of Lisp (NIL) for the VAX, which was headed by White. One of the stated goals of the NIL project was to fix many of the historic, but annoying, problems with Lisp while retaining significant compatibility with MacLisp. At about the same time, a research group at Stanford University and Lawrence Livermore National Laboratory headed by Richard P. Gabriel began the design of a Lisp to run on the S-1 Mark IIA supercomputer. S-1 Lisp, never completely functional, was the test bed for adapting advanced compiler techniques to Lisp implementation. Eventually the S-1 and NIL groups collaborated. For further information about the NIL project, see ``NIL---A Perspective.'' http://www.ai.sri.com/~delacaze/alu-site/alu/table/older.htm#NIL http://www.ai.sri.com/~delacaze/alu-site/alu/table/older.htm... >``NIL---A Perspective,'' JonL White, Macsyma User's Conference, 1979. https://en.wikipedia.org/wiki/NIL_(programming_language) https://en.wikipedia.org/wiki/NIL_(programming_language) >New Implementation of LISP (NIL) is a programming language, a dialect of the language Lisp, developed at the Massachusetts Institute of Technology (MIT) during the 1970s, and intended to be the successor to the language Maclisp.[1] It is a 32-bit implementation,[2] and was in part a response to Digital Equipment Corporation's (DEC) VAX computer. The project was headed by Jon L White,[3] with a stated goal of maintaining compatibility with MacLisp while fixing many of its problems.
- bear8642 5y agoT (opposite of NIL) also existed <https://en.m.wikipedia.org/wiki/T_(programming_language) https://en.m.wikipedia.org/wiki/T_(programming_language)>
- rtpg 5y agoI remember running into nil a lot when trying to use Clojure. My immediate sensibility was very "yuck". It allows for nice chaining of APIs but it seems to really just mess up the notion of optionality so much that I'm curious about how often it's a bug generator. I understand that the sort of spaghetti-code from constant nil checking would be painful, but I can also imagine a lot of hidden bugs when it comes to dealing with things like user-provided data. Would love to hear the experience from Lispers who also have experience with "null"-y issues in other languages.
- brundolf 5y agoPosted under another comment, but I've personally experienced it being a big problem where a Lisp interfaced with another language: https://news.ycombinator.com/item?id=27878200 https://news.ycombinator.com/item?id=27878200
- didibus 5y agoClojure doesn't have this problem, in Clojure nil is not the empty list, and the empty list is not nil. ;; Nil and empty list are different (nil? '()) ;> false ;; Empty list is not falsy (if '() true false) ;> true ;; Only nil is falsy (if nil true false) ;> false ;; Prints as empty list (pr '()) ;> ()
- brundolf 5y agoGood decision on their part. Unfortunately ours was CL-inspired
- didibus 5y agoI've got professional experience with null in Java, C#, and C++, as well as Clojure. From my experience "nil" in Clojure is the least "issue creating" of the bunch. Generally when "nil" is an issue in Clojure, it's actually from the Java side, where Java doesn't "do the right thing" for "nil" and just throws a "null pointer exception" instead. In Clojure it's mostly a problem when it hides that you just aren't getting something because of a bug, as opposed to because it truly isn't there. The most common one for me being map lookups: (get m :fooo) ;> nil Now everything after will "work", because everything handles "nil" input and generally does "the right thing", like treat it as false, or returns nil if given nil, or treats it as identity, etc. But you actually just messed up the key and that's why you are getting "nil", where you wanted to do: (get m :foo) ;> "foo" I'd say this is the most annoying case for "nil" for me in Clojure.
- PicassoCTs 5y agoNIHIL would have been more beautiful, but all programers are to lazy to even typ "Hi" so here we are.
- samplatt 5y agoI appreciate that the "to"/"too" typo could be part of the joke but know that it's still like sandpaper on the brain regardless.
- tankfeeder 5y agohttps://software-lab.de/doc/ref.html#nilSym https://software-lab.de/doc/ref.html#nilSym NIL in PicoLisp.
- TazeTSchnitzel 5y agoMaybe having only one value of this kind is an effective way to make it really obvious which problems occur when you don't have an Optional type? Those problems also happen in languages with several null or false-like values, but you discover them too late.
- mark_l_watson 5y agoI have been using Common Lisp since about 1982 and it is probably my favorite language both because I am used to it and because old code usually runs forever without modification. That said, I now also often use Racket and Swift that both seem fresher, but don't have the extreme stability of CL. I often use Common Lisp libraries that haven't been touched by the creators for 5 or 10 years (or more). I once wrote up a little rant about future software being ancient, with no changes for centuries. What might the far future be like? I was imagining a world where security and robustness were considered much more valuable than a never ending flow of new features. Could popular programs developed over many centuries evolve to a point where it made no sense to modify them?
- User23 5y agoI really like this perspective. One of the more, well, common, criticisms of Common Lisp is that the standard is stagnant. Reframing it as stable is great rhetoric and also sound dialectic. I've only been using CL for about half as long as you have, but I still appreciate that stability greatly. Also, it's not as if CL deprives neophiles of opportunity[1]. [1] https://lisp-journey.gitlab.io/blog/state-of-the-common-lisp-ecosystem-2020/ https://lisp-journey.gitlab.io/blog/state-of-the-common-lisp...