I wonder if it would be possible for the compiler to recognize that the nested type doesn't depend on the generic parameter, and therefore treat the nested type as non-generic. That would also mean that you'd have to be permitted to say Foo.Bar without being required to specify G.
This would require seeing all method bodies on the class, which might not even be possible if there are extensions that add new methods from outside the module, or in other source files. It's not worth the complexity. If you don't want a nested type to capture generic arguments, it should not be a nested type.
If you're not going to make use of the generic placeholder, you can use a dummy conformer.
class Foo<G: CustomStringConvertible> {
struct Bar where G == Never {
var something: Bool
}
}
extension Never: CustomStringConvertible {
public var description: String { fatalError() }
}
The other thing I was saying was to make a new type with the word Any prepended. It's not nested in Foo.
enum AnyFoo {
struct Bar {
var something: Bool
}
}
i too really want to do this oftentimes, as well as a related use case: nesting a protocol inside another type.
it is not that i care about it being a nested type, it is because of the - at first glance, unrelated - problem where we cannot define more than one top-level type per module, if the module has the same name as the type.
for example, if i have a module named Cromulence, and a type
struct Cromulence<T>
i cannot add a second, non-generic type alongside it, such as:
struct CromulenceOptions
there are really only three possible workarounds for this problem, none of which are satisfactory to me.
we can nest the options type in Cromulence<T> anyway, but we give up on the ability to reuse options between Cromulence<T>.Options and Cromulence<U>.Options.
we can rename the module itself to something like CromulenceModule. but this implies that adding a second top-level type to a module requires a module rename, which can be quite disruptive given how often this situation arises. and if CromulenceOptions later migrates out of CromulenceModule for an unrelated reason, we must once again rename CromulenceModule back to Cromulence for elasticity’s sake.
we can place CromulenceOptions in a separate module that Cromulence depends on and re-exports. but this usually results in a lot of unwanted @inlinable/@frozen proliferation, and forces many APIs to become public that i would rather not become public.
option #1 is obviously an inferior design, and i have gradually come to recognize option #2 as an enormous waste of time and source of unjustified API churn. so i have more or less settled on #3 as my preferred workaround.
but in my opinion the real solution to this problem is not to “fix” option #1 by allowing non-generic nested types, the real solution would simply be to allow eponymous modules to contain more than one top-level type.
Constraining a nested type to one particular outer type effectively makes the type non-generic. However, the outer type is still generic—you can just use it implicitly because the compiler knows there's only one option for the nested type. You can use any type for the generic removal.
i am definitely going to start doing this more. one issue i’ve observed is documentation tooling will vacuum up the sameType constraint and parrot it everywhere on all of Bart’s APIs. but this is a surmountable problem that should be addressed with better tooling heuristics.
Ever wanted to have a static var in a generic type? With the above Jessy's trick it's now possible:
struct Foo<T> {
// static var foo = 0 // 🛑 Static stored properties not supported in generic types
struct Bar where T == Never {
static var staticVar: Int = 0
}
static var staticVar: Int {
get { Foo<Never>.Bar.staticVar }
set { Foo<Never>.Bar.staticVar = newValue }
}
}
Foo<Int>.staticVar = 42
print(Foo<Float>.staticVar) // 42