Leave less undefined in the specification. In particular, there should be more defined cases for errors in safe code.
Complex type upgrading is just broken. The spec is inconsistent.
sort should have been called nsort, to be consistent with the names of other potentially side-effecting operations. SORT would not alter its argument.
If defpackage has no :use clause, it would be equivalent to (:use), not implementation-defined. Also: make package local nicknames part of the standard.
Many built ins functions should be generic and allow user defined methods if at least one argument is not of a standard-defined class. This should not have an efficiency penalty.
Many built-in classes should be user extensible, or at least have internal generic functions that can be extended. The exemplar here is user defined sequences in SBCL. This would also be useful for hash tables and streams.
Pathnames tried to be general and failed. They need a revamp.
Add support for multiple threads, concurrency, unicode.
Although it saddens me to say it, get rid of the punning of NIL and false, and make CAR/CDR not work on NIL.
Although it's not part of the standard, have a more robust social mechanism for curating and maintaining a semi-standard library, just outside the standard.
Packages should allow weakness, so that if a weak symbol is not used anywhere it can be GCed from the package. This could help with tree shaking.
> Add support for multiple threads, concurrency, ..
technically, already have that via lisp's take on decorators/generators. [1]
What lisp doesn't have, per pre-dating just about everything in CS, is way to associate the lisp take on threads/concurrency with underlying os/hardware (without modifying/recompliling language).
what would be nice is an alternate REPL namespace that can be 'reloaded to garbage collect, independent of standard REPL' where things that are split between OS/REPL/card reader operator can be placed. aka device drivers, thread/cpu/gpu specs. aka erlang/elixir extention
multi-threads, concurrency would be a process namespace & standard command / data name spaces could be mapped by programmer to how wanted to use in process name space.
Using "box of punch cards" as REPL, "punch card machine" as 'solo cpu' and "device operator" as handler for everything else.
Lisp just does the punch card box. Scheme does "the box" with access to the "punch card machine" and emacs covers the whole REPL, 'solo cpu', 'device operator' stuff
So, common lisp needs a 'device operator' namespace.
Some sort of governing entity. The ANSI standard has left the language totally dead in the water, and is the single biggest thing holding back this really quite beautiful language
Package local nicknames. The situation that libraries like Alexandria are in is really sad.
Nested namespacing/packages.
The ability to alias imports, like in Python.
In the same vein, most symbols in the spec should be moved into a standard library.
A general equality predicate which can be specialized by the user.
Just replace multiple-value-returns with structs, they don't add anything useful.
Standardize some variant of arrow macros.
Every form which introduces a binding should be able to take a (optional) type specifier. It'd be great if compilers could use this option to remove generic dispatch in certain compilation modes.
Algol based languages -> 1st order logic (implicit 'single' return)
algol languages array/stack with 'struct' abstract ('struc' abstract converted by compiler to ether parallel array(s) and/or array byte grouping per array ordinal index offset)
Lisp languages -> 2nd order logic (implicit nil/1 or many returns)
lisp is a tree / heap language where tree node is the 'implied' struct.
tree/heap much more flexible than fixed size array of strucs.
lisp -> 1-n; s-expression 1st element ins () is the "memory index"
structs -> n * M; "m-expression" 1st element outside of () is the "memory index".
cons struc vs. cons lisp () is apples/oranages comparison.
if convert both the 'cons struc' and the cons lisp () to same byte vector/stack or equivalent byte tree/heap, then yes, both 'cons' functions are equivalent.
higher order equivalent of mealy state machine vs. moore state machine. [1]
side note:
( 1 - n ) vs (n * m) is context reference to ploting base 2 log(x).
Another take is that a bunch of clever people not only got the job done, but recognized that their role is done and went their separate ways.
A similar thing happened in hardware with the HDMI spec.
The worst thing in programming languages is these insipid language committees that refuse to disband. If you look at C and C++, it's obvious the main thing they care about above all is their self-preservation: the ability to continue meeting and tinkering with languages that everyone else critically depends on being stable.
The goal of no technical committee should be its own self-preservation.
This is so true. Once it's done: stop working on it. All this instability serves nobody in the longer term. At the same time: lots of stuff is released half baked or worse and as a result you get a decade of goal post moving and backwards incompatibility for free.
I developed a language called TXR Lisp, which has ties with Common Lisp in many areas. If you look at the reference manual, which is conveniently one giant manual page available in HTML form, there are numerous dialect notes that mention ANSI Common Lisp; a chunk of that would likely be things I might change in Common Lisp.
Some things I would fix without changing Common Lisp:
* redefine "binding" as being an association between a symbol and a location.
* I'd make (setq x y) have a predictable, documented effect when x is not a defined variable. It would cause x to be bound to its value cell, and place the value of y into that cell, and that would be the same as (setf (symbol-value 'x) y).
* pick up some outstanding issues and resolve them, like this one: during a dynamic control transfer, an exit point is identified for that control transfer and then unwinding takes place. It is not specified in CL whether the intermediate exit points (the ones not selected for the transfer which are being skipped) are torn down before the control transfer takes place, or torn down as-you-go during unwinding. I'd fix that as being tear-down-as-you-go. This affects situations when an unwind-protect intercepts a control transfer and then tries to initiate a new one to a different exit point.
* I'd fix the order of evaluation of a function binding relative to the arguments. We know that in (f x y z), the x y z are evaluated in left to right order, if f is a function. However, we don't know whether the f binding is resolved before or after the arguments. I'd pick one order and require it; probably before the arguments, though that requires the longest possible temporary storage. Obviously there are situations in which the binding isn't evaluated each time the function is called, like when it has been inlined.
* loop would support application-defined clauses.
* structs and CLOS would be integrated together; conditions would be CLOS instances.
* in the area of pathnames, I would have the language specify precisely how POSIX and Windows paths correspond to pathname objects. Every implementation would have to follow that. For instance, it couldn't be that one implementation sees "foo.c" has having a type "c", and another one doesn't. Everything would be specified down to what happens with double slashes, and trailing slashes.
* string literals would support escape sequences for special characters. Also, Unicode would be required. Implementations would have to support UTF-8 code.
* string literals would support a way of splitting across multiple lines, such that the reader eats the leading indentation. Thus, the format function wouldn't need a hack for this.
lisp loop is a macro, can be modified to support above request.
> * in the area of pathnames, I would have the language specify precisely how POSIX and Windows paths correspond to pathname objects.
Elang/elixir approach where (pathname <file stats parameter>) provides a standard way to access file/path without having to know what the underlying file system is (unless check atrributes)
> * structs and CLOS would be integrated together; conditions would be CLOS instances.
strucs are just segmented arrays or multi-values in (). (condit ()) is a 'clos instance'.
The way python sets up it's class/object system is similar to lisps, just not using lisp ().
> ... we don't know whether the f binding is resolved before or after the arguments. ...
whats order in a tree/heap, other than way tree/heap traversed.
why not make use of setq/quote if binding order is important?
> * redefine "binding" as being an association between a symbol and a location.
first item in () s-expression is the symbol/location association. Doesn't # provide a way to get at what the symbol/location association is?
> ... I'd fix that as being tear-down-as-you-go. ...
Traditional lisp, nothing is garbaged reclaimed until exit REPL.
A CL implementation's loop macro cannot be modified without modifying the implementation, but your application can carry its own loop macro. Possibly, such a loop macro can generate code written in the standard loop macro, so that (enhanced-loop <standard-clauses...>) just emits (loop <standard-clauses...>) but if it detects nonstandard clauses, it expands them into standard clauses.
Lisp being what it is, you are rarely at the mercy of the implementation, when it comes to a language feature, unless the feature has to do with accessing some semantics deep in the implementation, and the problem is with that semantics and not the porcelain.
It is still undesirable for programmers to bring their ehanced-loop to the table. Because Bob has bob-loop, and Amy has amy-loop and they would like to combine their code where both bob-loop and amy-loop features are used not just in the same code but in the same loop. That just turns into a PITA. If they are lucky, amy-loop has some awkward syntax for specifying that a given invocation should generate code for some loop other than cl:loop, so they write (amy-loop :target bob-loop ...). Then amy-loop transforms all the amy-loop clauses it sees, leaves alone anything it doesn't understands and spits out bob-loop syntax. (This is not easy; skipping unfamiliar, non-standard loop syntax is going to involve guesswork because the clauses aren't nicely parenthesized!)
That would all be simpler if they could just write custom clauses for standard loop. Bob would write his in the bob-lib package; Amy in amy-lib. Everything works together out of the box; if you have bob-lib and amy-lib, you can use loop with bob-loop and amy-loop clauses.
I don't substantially understand the rest of your remarks, except to the extent that certain key words in my remarks serve as their prompts. The holidays being over, you should be able to get a medication refill now.
Not explicitly noted in original message was, reply to remarks were about examples of how that already exists in lisp without need to modify lisp.
'basic' loop in lisp is tree/heap traversal from initial outer ().
The standard loop macro just provides appropriate way/window dressing to quickly comprehend what's being done (aka while, counter loop, do while, repeat, etc).
What's refered to as generators in other languages, is implicit in how lisp traverses ().
per return a struct and not a list:
Generator approach would 'store the list of values' and return a single struct every time called until no more structs from 'original turn'.
traversal of () is the 'generic loop' in lisp. (clos or not)
set/quote discussion also left out the needed eval command.
stated 'rebinding' is already implicet in s-expressions used in lisp. request as given is an lvalue = rvalue(s) interpretation (m-expression). s-expressions are core of lisp. convertion lisp to use m-expressions instead of s-expressions would mean creating a new language.
> basic' loop in lisp is tree/heap traversal from initial outer ().
That's completely false, except that loops can be used to code a tree traversal.
Looping is a control construct centered around a backward branch, updates to iteration variables and termination testing. Loops can compile code that has nothing to do with the heap.
Why prefer a non-functional construct over a functional construct in a functional language?
The lisp loop macro simplifies the s-expression tree/heap traversal for benifit of coder/reader. The lisp loop macro is implilmented based on s-expression tree traversal.
Trees are used with closures. Heaps are used with objects. (lisp & non-lisp)
In lisp can debate what's an 'object', but multiple thunks are managed through a
heap structure.
Tree (or heap form of tree) order traversals are forms of binary backward branching.
Tree traversal can be structured so 'iteration variables' are not needed. Default lisp termination is 'NIL'
If only returning a single struc (with no memory management), then heap not required.
If returning multiple strucs in a list/tree (aka multiple s-expressions), then the s-expression 'thunks' will be managed via heap.
* The :test argument of various sequence processing and other functions would default to equal, not eql. The eql choice is incredibly poor. It doesn't benefit programs that use numbers, symbols or characters as keys, just breaks programs that use lists or strings.
* I would fix the equal function's inconsistencies, like how it recurses across conses, but not vectors.
* I would clean up the following issue in the CL condition system: certain behaviors of condition raising are implemented in specific functions. In CL, if you want an unhandled error condition to drop into the debugger, you have to signal it using the error function. It's that function which performs the callback. If you signal the condition with signal, and it is not handled, then signal just returns. WTF? I would fix that, so that all manner of signaling an error condition will go into the debugger or terminate or whatever.
31 comments
[ 0.18 ms ] story [ 98.8 ms ] threadComplex type upgrading is just broken. The spec is inconsistent.
sort should have been called nsort, to be consistent with the names of other potentially side-effecting operations. SORT would not alter its argument.
If defpackage has no :use clause, it would be equivalent to (:use), not implementation-defined. Also: make package local nicknames part of the standard.
Many built ins functions should be generic and allow user defined methods if at least one argument is not of a standard-defined class. This should not have an efficiency penalty.
Many built-in classes should be user extensible, or at least have internal generic functions that can be extended. The exemplar here is user defined sequences in SBCL. This would also be useful for hash tables and streams.
Pathnames tried to be general and failed. They need a revamp.
Add support for multiple threads, concurrency, unicode.
Although it saddens me to say it, get rid of the punning of NIL and false, and make CAR/CDR not work on NIL.
Although it's not part of the standard, have a more robust social mechanism for curating and maintaining a semi-standard library, just outside the standard.
Packages should allow weakness, so that if a weak symbol is not used anywhere it can be GCed from the package. This could help with tree shaking.
technically, already have that via lisp's take on decorators/generators. [1]
What lisp doesn't have, per pre-dating just about everything in CS, is way to associate the lisp take on threads/concurrency with underlying os/hardware (without modifying/recompliling language).
what would be nice is an alternate REPL namespace that can be 'reloaded to garbage collect, independent of standard REPL' where things that are split between OS/REPL/card reader operator can be placed. aka device drivers, thread/cpu/gpu specs. aka erlang/elixir extention
----
[1] : https://stackoverflow.com/questions/32956033/is-there-a-stra...
multi-threads, concurrency would be a process namespace & standard command / data name spaces could be mapped by programmer to how wanted to use in process name space.
error(s) would be done in a 'error' namespace.
So, common lisp needs a 'device operator' namespace.
Package local nicknames. The situation that libraries like Alexandria are in is really sad.
Nested namespacing/packages.
The ability to alias imports, like in Python.
In the same vein, most symbols in the spec should be moved into a standard library.
A general equality predicate which can be specialized by the user.
Just replace multiple-value-returns with structs, they don't add anything useful.
Standardize some variant of arrow macros.
Every form which introduces a binding should be able to take a (optional) type specifier. It'd be great if compilers could use this option to remove generic dispatch in certain compilation modes.
Oh yes they do! They allow to add a return value without modifying all the calling sites. Extremely useful.
algol languages array/stack with 'struct' abstract ('struc' abstract converted by compiler to ether parallel array(s) and/or array byte grouping per array ordinal index offset)
Lisp languages -> 2nd order logic (implicit nil/1 or many returns)
lisp is a tree / heap language where tree node is the 'implied' struct.
tree/heap much more flexible than fixed size array of strucs.
structs -> n * M; "m-expression" 1st element outside of () is the "memory index".
cons struc vs. cons lisp () is apples/oranages comparison.
if convert both the 'cons struc' and the cons lisp () to same byte vector/stack or equivalent byte tree/heap, then yes, both 'cons' functions are equivalent.
higher order equivalent of mealy state machine vs. moore state machine. [1]
side note: ( 1 - n ) vs (n * m) is context reference to ploting base 2 log(x).
(1 - n ) can never exceed 1.
N * M, relative to (1 - n) is unbounded.
=====
[1] : https://www.geeksforgeeks.org/difference-between-mealy-machi...
lisp approach to dectorators / generators disucssion. https://stackoverflow.com/questions/32956033/is-there-a-stra...
A similar thing happened in hardware with the HDMI spec.
The worst thing in programming languages is these insipid language committees that refuse to disband. If you look at C and C++, it's obvious the main thing they care about above all is their self-preservation: the ability to continue meeting and tinkering with languages that everyone else critically depends on being stable.
The goal of no technical committee should be its own self-preservation.
Some things I would fix without changing Common Lisp:
* redefine "binding" as being an association between a symbol and a location.
* I'd make (setq x y) have a predictable, documented effect when x is not a defined variable. It would cause x to be bound to its value cell, and place the value of y into that cell, and that would be the same as (setf (symbol-value 'x) y).
* pick up some outstanding issues and resolve them, like this one: during a dynamic control transfer, an exit point is identified for that control transfer and then unwinding takes place. It is not specified in CL whether the intermediate exit points (the ones not selected for the transfer which are being skipped) are torn down before the control transfer takes place, or torn down as-you-go during unwinding. I'd fix that as being tear-down-as-you-go. This affects situations when an unwind-protect intercepts a control transfer and then tries to initiate a new one to a different exit point.
* I'd fix the order of evaluation of a function binding relative to the arguments. We know that in (f x y z), the x y z are evaluated in left to right order, if f is a function. However, we don't know whether the f binding is resolved before or after the arguments. I'd pick one order and require it; probably before the arguments, though that requires the longest possible temporary storage. Obviously there are situations in which the binding isn't evaluated each time the function is called, like when it has been inlined.
* loop would support application-defined clauses.
* structs and CLOS would be integrated together; conditions would be CLOS instances.
* in the area of pathnames, I would have the language specify precisely how POSIX and Windows paths correspond to pathname objects. Every implementation would have to follow that. For instance, it couldn't be that one implementation sees "foo.c" has having a type "c", and another one doesn't. Everything would be specified down to what happens with double slashes, and trailing slashes.
* string literals would support escape sequences for special characters. Also, Unicode would be required. Implementations would have to support UTF-8 code.
* string literals would support a way of splitting across multiple lines, such that the reader eats the leading indentation. Thus, the format function wouldn't need a hack for this.
* FFI would be in the language.
lisp loop is a macro, can be modified to support above request.
> * in the area of pathnames, I would have the language specify precisely how POSIX and Windows paths correspond to pathname objects.
Elang/elixir approach where (pathname <file stats parameter>) provides a standard way to access file/path without having to know what the underlying file system is (unless check atrributes)
> * structs and CLOS would be integrated together; conditions would be CLOS instances.
strucs are just segmented arrays or multi-values in (). (condit ()) is a 'clos instance'.
The way python sets up it's class/object system is similar to lisps, just not using lisp ().
> ... we don't know whether the f binding is resolved before or after the arguments. ...
whats order in a tree/heap, other than way tree/heap traversed.
why not make use of setq/quote if binding order is important?
> * redefine "binding" as being an association between a symbol and a location.
first item in () s-expression is the symbol/location association. Doesn't # provide a way to get at what the symbol/location association is?
> ... I'd fix that as being tear-down-as-you-go. ... Traditional lisp, nothing is garbaged reclaimed until exit REPL.
Lisp being what it is, you are rarely at the mercy of the implementation, when it comes to a language feature, unless the feature has to do with accessing some semantics deep in the implementation, and the problem is with that semantics and not the porcelain.
It is still undesirable for programmers to bring their ehanced-loop to the table. Because Bob has bob-loop, and Amy has amy-loop and they would like to combine their code where both bob-loop and amy-loop features are used not just in the same code but in the same loop. That just turns into a PITA. If they are lucky, amy-loop has some awkward syntax for specifying that a given invocation should generate code for some loop other than cl:loop, so they write (amy-loop :target bob-loop ...). Then amy-loop transforms all the amy-loop clauses it sees, leaves alone anything it doesn't understands and spits out bob-loop syntax. (This is not easy; skipping unfamiliar, non-standard loop syntax is going to involve guesswork because the clauses aren't nicely parenthesized!)
That would all be simpler if they could just write custom clauses for standard loop. Bob would write his in the bob-lib package; Amy in amy-lib. Everything works together out of the box; if you have bob-lib and amy-lib, you can use loop with bob-loop and amy-loop clauses.
I don't substantially understand the rest of your remarks, except to the extent that certain key words in my remarks serve as their prompts. The holidays being over, you should be able to get a medication refill now.
to much line spacing is a python thing, lisp is to parenthetical.
'basic' loop in lisp is tree/heap traversal from initial outer ().
The standard loop macro just provides appropriate way/window dressing to quickly comprehend what's being done (aka while, counter loop, do while, repeat, etc).
What's refered to as generators in other languages, is implicit in how lisp traverses ().
per return a struct and not a list: Generator approach would 'store the list of values' and return a single struct every time called until no more structs from 'original turn'.
traversal of () is the 'generic loop' in lisp. (clos or not)
set/quote discussion also left out the needed eval command.
stated 'rebinding' is already implicet in s-expressions used in lisp. request as given is an lvalue = rvalue(s) interpretation (m-expression). s-expressions are core of lisp. convertion lisp to use m-expressions instead of s-expressions would mean creating a new language.
That's completely false, except that loops can be used to code a tree traversal.
Looping is a control construct centered around a backward branch, updates to iteration variables and termination testing. Loops can compile code that has nothing to do with the heap.
The lisp loop macro simplifies the s-expression tree/heap traversal for benifit of coder/reader. The lisp loop macro is implilmented based on s-expression tree traversal.
Trees are used with closures. Heaps are used with objects. (lisp & non-lisp)
In lisp can debate what's an 'object', but multiple thunks are managed through a heap structure.
Tree (or heap form of tree) order traversals are forms of binary backward branching.
Tree traversal can be structured so 'iteration variables' are not needed. Default lisp termination is 'NIL'
If only returning a single struc (with no memory management), then heap not required.
If returning multiple strucs in a list/tree (aka multiple s-expressions), then the s-expression 'thunks' will be managed via heap.
* The :test argument of various sequence processing and other functions would default to equal, not eql. The eql choice is incredibly poor. It doesn't benefit programs that use numbers, symbols or characters as keys, just breaks programs that use lists or strings.
* I would fix the equal function's inconsistencies, like how it recurses across conses, but not vectors.
* I would clean up the following issue in the CL condition system: certain behaviors of condition raising are implemented in specific functions. In CL, if you want an unhandled error condition to drop into the debugger, you have to signal it using the error function. It's that function which performs the callback. If you signal the condition with signal, and it is not handled, then signal just returns. WTF? I would fix that, so that all manner of signaling an error condition will go into the debugger or terminate or whatever.