i have a generic type BSON.DocumentEncoder
:
extension BSON
{
@frozen public
struct DocumentEncoder<CodingKey>
where CodingKey:RawRepresentable<String>
}
it has a generic subscript that can use some coding key type, that is not necessarily Self.CodingKey
.
extension BSON.DocumentEncoder
{
@inlinable public
subscript(with key:some RawRepresentable<String>) -> BSON.FieldEncoder
{
_read { yield self.output[with: .init(key)] }
_modify { yield &self.output[with: .init(key)] }
}
}
but this subscript has a problem, in that it cannot easily use type inference or leading dot syntax. so i would like the type checker to first assume the generic type is Self.CodingKey
, and only consider other RawRepresentable
types if Self.CodingKey
does not typecheck.
one possibility is to add a concretely-typed overload to subscript(with:)
.
extension BSON.DocumentEncoder
{
@inlinable public
subscript(with key:CodingKey) -> BSON.FieldEncoder
{
_read { yield self.output[with: .init(key)] }
_modify { yield &self.output[with: .init(key)] }
}
}
but i do not want to do this, because this subscript is widely discussed in the package’s documentation, and if i add an overload, it will be exceptionally hard to talk about the subscript with symbol links.
are there any better ways to provide a default type hint for this generic API?
2 Likes
I run into this limitation periodically too.
Another variation is with generic return types, e.g. I occasionally use a generic RangeReplaceableCollection
but usually the caller is happy with Array
. Unfortunately, there's no good way to express that default (short of method overloading) and so callers tend to have to be unnecessarily explicit (e.g. casting the result as Array
, or explicitly typing the destination variable, etc).
I had the same trouble several times. Currently it is allowed to provide a default generic type at either type level or value level, but not both at the same time:
func foo<Lock>(lock: Lock = NSLock()) { // ok
}
func bar<T>(type: T.Type = String.self) { // ok
}
extension BSON.DocumentEncoder {
subscript<K: RawRepresentable<String>>(with key: K, _ type: K.Type = CodingKey.self) -> String {
""
}
// Error: Cannot use default expression for inference of 'K.Type' because it is inferrable from parameters #0, #1
}
Finally I made several overloads. Unfortunately, sometimes it was needed to copy-paste implementation instead of having one generic.
May be there are some upcoming changes in Swift 6 allowing to do what is needed @Slava_Pestov
I wrote a pitch, years ago, for a feature that could fulfil this need: Pitch: Scoped Functions. The pitch was not very well written, so I'm not surprised it didn't fly.
The goal was let an API favor one specific value for the leading dot syntax, inside the body of a closure, or... in an autoclosure
In your BSON example, the pitch would let you write:
extension BSON.DocumentEncoder
{
@inlinable public
subscript(
with key:@autoclosure @scoped (CodingKey.Type) -> some RawRepresentable<String>
) -> BSON.FieldEncoder { ... }
}
enum MyEnum: String { case bar }
encoder[.foo] // OK: CodingKey.foo
encoder[MyEnum.bar] // OK: MyEnum
EDIT: fixed the sample code so that .foo
is understood as CodingKey.foo
by scoping the key
to CodingKey.Type
.
1 Like