A few thoughts
I think macro definitions for all the define- functions can be avoided like this:
\version "2.19.80"
\include "oll-core/package.ily"
#(define-macro (with-options func-def-proc vars preds rulings . body)
`(,func-def-proc ,(append '(opts) vars) ,(append '(ly:context-mod?) preds)
(let* ((rules ,rulings)
(props (context-mod->props rules #t opts)))
. ,body)))
testRules =
#(with-options define-void-function () ()
`((ind ,number? 5)
(target ,symbol?)
(payload)
(accepted-arg ,fraction? opt)
(accepted-without-type opt)
(msg ,string? "No message given"))
(pretty-print props))
\testRules \with {
msg = "Something"
unk = "Unknown option"
target = something
% accepted-arg = 7/4
% accepted-without-type = #(ly:make-moment 3/16)
}
With respect on how to write the rule-set, based on your input I see a few possibilities:
1) A very slight modification of its current form
`((rule-enforcement strict)
(ind ,number? 5)
(target ,symbol?)
(payload)
(accepted-arg ,fraction? opt)
(accepted-without-type opt)
(msg ,string? "No message given"))
This keeps the "list of lists" approach you wanted. I think that the parsing of the options could be set to 'flexible' by default and it can be made strict adding that as the first element of the list. Alternatively, a rule-enforcement element may be required.
2) Optional arguments could be anticipated with an opt instead of having it at the end:
`((ind ,number? 5)
(target ,symbol?)
(payload)
(opt accepted-arg ,fraction?)
(opt accepted-without-type)
(msg ,string? "No message given"))
This requires the same input as before but feels clearer to my eyes. I see what you mean about the POV defining what can be called optional. As I described earlier, the "caller" POV feels more intuitive for me but that may just be personal taste. Without more opinions it's difficult to tell.
3) The "elimination of unnecessary parens" is indeed a very minor thing. That approach felt instantly familiar because it resembles how function predicates are defined, in that only the proc is written when there is no default value, but the proc and the default value are parenthesized when they are needed together. In the same logic, I thought the key could be by itself when all one needed was to communicate "this is optional" or "this is required", and parenthesized with more information when needed.
My previous idea was merely just these three together. As you pointed out, I think the first is the one of real importance, and the others are more in the realm of QOL suggestions.
For now I'll await your thoughts, and I'll open a pull request later.