I am using Embedded Swift rather than the full language for game development because I find it valuable to compile a small binary for a platform like wasm and have it load instantly on any browser. But this mode forces additional restrictions that I would say are bit much for this use case, like not being able to query the name of a class or use any.
There exist use cases for a more portable runtime which don't imply being as resource constrained as embedded software. Are those going to be considered eventually? Or is Swift strictly either an application or embedded language with no in-between?
That's sort of the fundamental tradeoff though -- you're giving up certain dynamic features for the ability to generate a small standalone binary that does not depend on the Swift runtime.
In principle it would be great if you could pick and choose the level of dynamism you want instead of "all or nothing" like we have to today, and I don't think there is any fundamental reason we can't have that in the future, if someone wants to work on that.
However, existential types pretty much do require the full Swift runtime, because you need to be able to instantiate runtime metadata and then use the metadata to manipulate values in a completely abstract away.
At the very least I'd love to be able to use existentials with classes, since with how I use value types it's much easier to ensure all types are known at compile time, and some use cases I had for any like SwiftUI style stacks can be replaced with variadic generics instead (at least I hope they can, last time I tried they weren't fully implemented).
But with classes this is really annoying, it's hard to express certain things with only single inheritance.
Since embedded Swift supports vtable dispatch for classes, it could conceivably also implement class-constrained existentials using the same technique (that is, for protocols that have an AnyObject or an explicit superclass bound). Since a class instance is always a reference counted pointer, there's no abstraction over the value representation itself.
protocol Storable: Entity {}
class StorageEntity: Entity {
var capacity: Int { 0 }
private var storage: [any Storable] = []
...
}
In this case the protocol doesn't require anything so I could make it a boolean on all entities that they override, and have some code that ensures they can't be stored by other entities, but I like having this expressed in the type system (even if it's probably less efficient)
In some cases where the exact type is known I'd get a compile error if I tried to store it somewhere and it isn't allowed
Without dynamic casting and metatype support, though, the amount of information we'd need to abstractly manipulate values is still greatly decreased, since we just need the value witness information rather than a full unique metadata record. And since Embedded Swift isn't ABI-bound to require value witness tables, it should be able to use the new value witness bytecode implementation for most purposes. We still wouldn't be able to arbitrarily open existentials, only call methods that are direct requirements of the stated protocols that don't have any unbound associated types which can be dispatched through the witness table, but that should cover a wide range of use cases.
I've been using a variation of embedded mode four some time, via the Swift for Arduino project, which has similar restrictions.
I've found in practice that most places where I wanted to use any can be achieved Ina different way with more concrete type definitions. For example, with your sample, it could be defined as:
protocol Storable: Entity {}
class StorageEntity<Element: Storable>: Entity {
var capacity: Int { 0 }
private var storage: [Element] = []
...
}
It loses the ability to contain literally any type in one bucket, so maybe that doesn't meet you use case. But in my experience there are ways to narrow your scope and work around those limitations.
Yes, generics wouldn't fit my use case sadly. A StorageEntity would be anything like a chest or backpack, and I think a chest that can only store, say, swords wouldn't be a very useful chest
The reason I want to have Storable as a marker protocol rather than using inheritance and requiring that only Item subclasses can be stored, is that eventually I might want to for example put a cat in a backpack and inheritance would fall apart. (weird example but I think you see what I mean)
There's also places in my code where I could replace arrays of existentials with variadic generics.
So you are right, I don't need them, but I could avoid having to deal with more complicated code or fighting with inheritance
I was only testing my wasm and rendering code with a simple game jam game, and it was already taking 14.5s to build on an M2 Max. I wouldn't want to make this problem even worse by generating code.
Writing these enums by hand would probably make compilation slower by itself given how much this time increased with every feature I added.
Embedded Swift, I am unable to build normal Swift with new toolchains due to some incompatibility preventing the Darwin module from working. I am only able to compile for wasm.
Unclear to me, what I've seen so far is that compile times are comparable. There's reasons why Embedded Swift could be slower to compile (has to do more work, e.g. mandatory specialization) but also reasons why it could be faster (can be more lazy about actually compiling code based on what's in use)...
It may or may not maker compilation slower using enums. Using any Storable has a cost because the compiler has to do more complex type checking/call site resolution when calling functions with existential type signatures. Using enum would make that process simpler to resolve.
Sure, but that's when writing the enums manually, and handling it with macros or writing down a large number of enum cases is not something I want to do; I've been waiting for Xcode to finish indexing SwiftSyntax for about 15 minutes now. I would rather not