suppose i have an application with a dependency graph that looks like:
.target("FrameworkCore"),
.target("Framework",
dependencies:
[
"FrameworkCore",
.product(name: "NIOCore", package: "swift-nio")
]),
.target("ApplicationCore", dependencies: ["FrameworkCore"]),
.target("Application", dependencies: ["ApplicationCore", "Framework"]),
FrameworkCore
the FrameworkCore
target defines a MyDecodingContainer<Storage>
type, which is generic over some storage type, whose subscript(_:)
will be called often.
this type is @frozen
, and all of its API is @inlinable
.
it also has a public MyDecodable
protocol, which has a generic init(from:)
requirement taking an instance of MyDecodingContainer
.
FrameworkCore/MyDecodable.swift
@frozen public
struct MyDecodingContainer<Storage>
where Storage:RandomAccessCollection<UInt8>
{
...
}
public
protocol MyDecodable
{
init(from:MyDecodingContainer<some RandomAccessCollection<UInt8>>)
throws
}
Framework
the Framework
target contains a type MyGenericScheme
, which is generic over some MyDecodable
type, and uses a specialization of MyDecodable
and MyDecodingContainer
s’ API where Storage == NIOCore.ByteBufferView
.
MyGenericScheme
is not @frozen
, and its API is not @inlinable
, at least not yet.
Framework
depends on NIOCore
, and NIOCore
takes a long time to clone and build. and it’s also annoying to repeatedly import NIOCore
everywhere, so targets that can depend on FrameworkCore
only should depend on FrameworkCore
and not Framework
.
Framework/MyGenericScheme.swift
import FrameworkCore
import NIOCore
public
struct MyGenericScheme<Element> where Element:MyDecodable
{
public
let element:Element
}
extension MyGenericScheme
{
public
init(bytes:ByteBufferView) throws
{
let decoder:MyDecodingContainer<ByteBufferView> = .init(bytes)
self.init(element: try .init(from: decoder))
}
}
ApplicationCore
ApplicationCore
is a application-specific target that defines a type Foo
that conforms to FrameworkCore.MyDecodable
.
ApplicationCore
does not depend on NIOCore
, and knows nothing about ByteBuffer
or ByteBufferView
. so clearly, Foo
’s MyDecodable
conformance must be @inlinable
in order to get anything resembling decent performance.
ApplicationCore/Foo.swift
import FrameworkCore
@frozen public
struct Foo
{
}
extension Foo:MyDecodable
{
@inlinable public
init(from:MyDecodingContainer<some RandomAccessCollection<UInt8>>)
throws
{
...
}
}
Application
finally, Application
brings the four components (ApplicationCore
, FrameworkCore
, Framework
, and NIOCore
) together, with a function bar(bytes:)
like the one below:
Application/Bar.swift
import ApplicationCore
import FrameworkCore
import Framework
import NIOCore
func bar(bytes:ByteBufferView) throws -> Foo
{
let scheme:MyGenericScheme<Foo> = try .init(bytes: bytes)
return scheme.element
}
my question is about how resilient MyGenericScheme
can be, while still maintaining acceptable performance.
- does
MyGenericScheme.init(from:)
need to become@inlinable
? - does
MyGenericScheme
itself need to become@frozen
?