@dynamicCallable with tailing defaulted parameters for literals #file, #function etc

Just a quick suggestion... following on from @dynamicCallable logger I was wondering if any thought had been given to usings defaulted parameters with static literals in @dynamicCallable types?

e.g:

@dynamicCallable
class Logger {

    func dynamicallyCall(withArguments args: [Int], file: String = #file, function: String = #function) {
         print(file, function)
         ...
    }

Is there any other way to achieve this effect with a @dynamicCallable?

Cheers

Using "static" callables could be a better option.

struct Foo {
  func callAsFunction(_ args: Int..., file: String = #file, function: String = #function) {
    print(args, file, function)
  }
}
Foo()(1, 2, 3) // it works!

However, #24299 has not been merged in master yet.

@rxwei thanks for your speedy response!

static callables look fantastic. But I really like the call site for my simple Logger type as I can label each argument without fuss. As swift can't reflect the name of the passed argument, I find myself chronically writing things like...

print("edits:", levenshtein.edits, "pathgen.count:", pathgen.count)
// (I don't actually use a raw `print` call):

I definitely prefer the @dynamicCallable class Logger syntax:

var log = Logger()
log(edits: levenshtein.edits, pathgenCount: pathgen.count)

The call doesn't shout at me "I'm logging now!"

I can, of course, pass the #function:

log(function: #function)

In your case, couldn't you also use a custom ExpressibleByStringInterpolation type?

Edit: Although I guess if you're not restricting the types of things being logged, you do need something a bit more dynamic.

@nuclearace thanks for the suggestion.

As of swift 5.1, string interpolation is broken in the dynamicallyCall(withArguments:) and dynamicallyCall(withKeywordArguments:) methods, so it's hard to be sure. But until the swift compiler has beefier reflection (if ever) manually writing out labels for debug and logged data, especially in testing and playground code, seems inevitable. See DefaultStringInterpolation cannot be used as an argument for DynamicCallable

I did manage to get interpolation working by using a class-based implementation of StringInterpolationProtocol on a custom type, but had to misuse ExpressibleByArrayLiteral in lieu of a ExpressibleBy<T> (this works for a fake ExpressibleByTuple too :woozy_face:) I'll post the code back on the original thread.

Here are a couple of callsite examples:

        log("\(0...1, label: "closed")", "\(2..<3, label: "halfOpen")") // using ExpressibleByStringInterpolation
        log(partialUpTo: [...4], partialFrom: [5...]) // using ExpressibleByArrayLiteral

I definitely prefer typing the second form. (the array notation notation is only needed if you want string interpolation support before swift 6.

I submitted a patch to cherry pick fixes for 5.1, so we’ll have to wait and see if it gets merged :slight_smile:

1 Like