Hi,

In the case of 'map' passing some arguments to 'f', what's the problem in defining both hygienically as importable/exportable syntax-rules as such?

(def-syntax [map f ls . params]
  (let-syntax-rule ([K reverse: rev count: cnt . rest-keylist]
  (map (λ (x) (begin (set! cnt (+1 cnt)) (f x . rest-keylist))) ls reverse: rev)
  )
 (comb (reverse:  #f count: *count*)
    (K) . params)
   ))
;; I took the liberty here of using some short-hands I normally use with syntax-rules

(map fun lst count: cnt foo: 42 reverse: #t)
=> (map (λ (x) (begin (set! cnt (+1 cnt)) (fun x foo: 42))) lst reverse: #t)

Where 'comb' is a suitably defined macro that sorts the list of key-value pairs given to it from the 3rd argument, according to the manifest specified as the 1st argument (so, reverse: shall be the first argument pair to the macro-continuation, 'K' and count: shall be the 2nd argument, mapped to the arguments of 'map' according to the body of 'K').  If the keyword arguments are not given, then the defaults from the manifest shall be used. Mandatory arguments would then be consumed at the top-level 'map' macro, while the rest 'params' would undergo this sorting.

All the unrecognised arguments would go into the rest-keylist, passed down to a similarly-defined macro for 'f' (I also took the liberty to addiing a counting feature here:-). This would work with no overhead (keywords are just syntax, arguments remain 100% positional) as long as 'f' is statically known as such keyword-handling macro as well.

So the macro interface as above would completely specify the keyword-handling behaviour, at no run-time costs, and can therefore be exported, imported etc. hygienically. One would only need to annotate the higher-order arguments with expected keyword macro interface declaration.

Kind regards,
Peter

On Mon, Oct 21, 2019 at 1:47 PM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
Am Mo., 21. Okt. 2019 um 09:53 Uhr schrieb Shiro Kawai <xxxxxx@gmail.com>:
>
> On Sun, Oct 20, 2019 at 8:50 PM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
>>
>>
>> If, say, `f', `f1', `f2', and `g' all intend to implement the same
>> `test:' protocol, then it makes sense for their respective libraries
>> to import the same `test:' identifier from a common library. Passing
>> down any value through such a `test:' argument by, say, `g', makes
>> only sense if the callee understands this particular type of value. In
>> other words, they have to cooperate beforehand anyway.
>>
>
> They should in the ideal world, but I'm highly skeptical that it will work in practice.
>
> Because when one start writing library for 'f1', he can't foresee which keyword arguments
> would be shared with future libraries, nor is able to know if there's someone developing
> independent library that uses the same keyword with the same purpose.  The same applies
> to 'f2'.  And they may not care each other's library---their interest isn't there.
>
> It's only the user who cares compatibility of f1 and f2.  Would the user ask the author
> of f1 and f2 to change their code to import the same binding?  But which library should
> they import from?  You don't want to introduce extra dependency, which means extra burden
> of mentenance, just to make a particular user happy.
>
> Or, suppose there's a huge library that provides 'f1' and keyword bindings.
> You write an independent library 'f2', but knows the meaning of 'f1's keyword argument and
> want to implement the same protocol.  But you don't want to depend on the entire 'f1' library
> just to share one keyword binding.  Would you ask the author of 'f1' to split their keyword bindings
> to a separate library?  It's not only just an extra libary, but they should also split the distribution
> package for them, since if a dependency tool tries to fetch the (f1 keyword) library you
> don't want it to pull entire f1 library.
>
> And then, the maintainer of f1 library decided to create an entire new version and changed
> the keyword name, and decided to drop support of old version.  You, on the other hand, need to
> want to keep old keyword name for your users.  You and the f1 author should coopetate
> and move the binding of original keyword from (f1 keyword) package to your f2 package.
>
> Oh wait, there's another f3 package that also depended on (f1 keyword) package, and they
> want to do the same!  Now you have to talk to f3 package author, to create a new common
> library to be used by f2 and f3 libarries.
>
> Nowadays, your software can depend on tens or even more than hundred of libraries, all of
> them evolves mostly independently.  I don't think it is feasible to create a common library
> for common meaning of each keywords.
>
> One can argue that, if you want to share interface, you should import from the same libary---
> but when people talk about such interface, it is a whole interface---procedure signature or
> common slots.  Each keyword is just a piece of it.  However, using hygiene to solve
> the problem, you have to keep your eye on every pieces, not on the interface as a whole.
> That makes the mentenance far more difficult.

While I understand your points that it is hard to develop a stable
interface independently, I don't see how this is an argument against
the possibility of hygienic keywords: With only non-hygienic keywords,
it just gets harder and one has no chance to rename identifiers when
libraries step on each other's toes.

On the other hand, if `f1'. `f2', and `f3' are developed by the same
authors, hygiene will be useful. Or, a library, say `f0', exporting
the keyword and on which `f1', `f2', and `f3' are based, can come from
a future SRFI.

But let me try to give a better example than the `test:' example.
Consider a version of `map' that takes a keyword argument `reverse:'.
When true, the resulting list will be reversed. Furthermore, `map'
shall take optional keyword arguments that are opaque to `map' and
passed down to the procedure called by `map'.

For example:

(map f ls reverse: #t foo: 42)

Here, `f' shall be called with the list item and the keyword argument
`foo:' bound to `42'. However, how can `map' whether to pass down a
keyword argument `reverse:' to `foo:' or whether to change its own
behavior?

If at least `reverse:' is hygienic, the problem is far less urgent.

-- Marc

>
>
>
>
>>
>> What hygiene allows is to have two different, independently created
>> test protocols in the same program. This won't be possible if `test:'
>> is a mere identifier.
>>
>> Anyway, I have also proposed on this list to allow other types than
>> identifiers as markers for keyword arguments. Only when you want
>> hygiene, you would use an identifier. Otherwise, you would use, say, a
>> keyword object as in SRFI 88. Macro writers can do the same.
>>
>> >
>> > That's why I'm claiming keyword arguments isn't much about identifiers but just an extension
>> > of ordinary arguments.  For positional parameters, the semantics of the first parameter differs
>> > completely among procedures, but we don't think it's conflating the protocol.  It is ok that the first
>> > argument of procedure f and the first argument of procedure g has completely different meanings,
>> > and it's callers responsibility to pass appropriate one.  If g takes a procedure that takes filename
>> > as the first argument, then it's caller's responsibility to pass such procedure to g.
>> > The same applies to keyword argument.  If g takes a procedure that takes logger: argument of
>> > a specific type of object, then it's caller's responsibility to pass such procedure.
>> >
>>
>> Keyword arguments could/would make it possible to treat some procedure
>> arguments in a uniform way. For position arguments, this is only
>> possible when we do whole-program transformations (see my CPS example
>> from somewhere above).
>>
>> Marc