Comment by newzino
The "all functions are automatically variadic" design is a nice simplicity win. No overloading, no arity mismatches at call sites - just a uniform calling convention.
The argsType struct with pointer array and count is essentially how varargs works at the ABI level in C anyway, you've just made it explicit. And having the type info alongside the pointers means you get runtime type checking without the caller needing to pass format strings or sentinel values like traditional C varargs.
The tradeoff is you lose static arity checking at parse time, but for an embedded scripting use case that's probably fine - you're validating at runtime anyway and the error messages can be more helpful than "wrong number of arguments."
Do you have plans for optional/default arguments, or is that outside the scope? With variadic-by-default it'd be natural to just check args.num and use defaults for missing ones.
Yes and the simplicity extends to function definitions too, since you don’t have to specify any type info. E.g.
f :: { ; print(args) }
Brevity is especially nice for inline/anonymous functions.
You can definitely use args.num, args.type[], and args.indices[] to figure out which optional parameters were passed, but I’ve decided that it’s usually easier to pass a full set of parameters into C and have the scripted wrapper handle the optional params. This is easy in Cicada because of ‘code substitution’ (one of the innovations I’m proudest of and if you’ve seen this elsewhere please let me know!). Example:
callC :: {
}Then you can call it with or without modifying the optional parameters from their default values.
callC(2, 3) | uses the default string
callC(2, 3; str = “modified param”)
callC() runs its arguments as a function, substituted into the params variable, allowing the arguments to modify params. This is weird and I haven’t seen it elsewhere, but it’s very useful.