Being familiar with CL, I can see these features came out of necessity, and I've used them in number
of occasions.  I do agree that the syntax can be a bit simpler if they don't need to keep the backard
compatibility.
Actually, I even see a feature missing from CL, which is to get &rest arguments minus processed
keyword arguments.  (Hence Gauche supports it).


On Thu, Oct 17, 2019 at 10:26 PM Lassi Kortela <xxxxxx@lassi.io> wrote:
> I checked with CLISP and #lisp, and they agree:
> optional arguments are filled before any keyword detection begins.

That's right.

(defun foo (&optional x &key y) (list x y))

(foo :y)       ; => (:Y NIL)
(foo :y :y 1)  ; => (:Y 1)

When you think through the ramifications, it's the logical choice when
keywords are self-evaluating objects, but it probably confuses everyone
at first.

Even more confusing is that keyword args can co-exist with a rest arg.
They receive the same portion of the arglist, the rest arg gets the raw
version and the keyword args the parsed one:

(defun foo (&optional x &rest r &key y) (list* x y r))

(foo)          => (NIL NIL)
(foo :y)       => (:Y NIL)
(foo :y :y 1)  => (:Y 1 :Y 1)

Finally there's &allow-other-keys, default values and supplied-p
parameters. I left all of this out of 177 on purpose, as I think it
confuses users without offering much benefit.

Another fun fact: Racket has required keyword arguments. In fact they
are required by default unless you give them a default value.