Konrad Malawski is a member of a team developing foundational server-side Swift libraries at Apple, with focus on distributed systems and concurrency.
Google Summer of Code (also known as GSoC) is a long-running mentorship program focused on introducing contributors to the world of open source development. This year marks the fifth time the Swift project has participated in GSoC.
During the 2022 edition of the program, we were lucky to work with five great contributors, all of which completed their assigned projects successfully. We would like to thank all the contributors – Amritpan, Felix, Fredrik, Kth and Sofía – for the time and passion they poured into their projects:
- Bootstrapping SwiftSyntaxBuilder
- Improving the debug output of the type inference algorithm
- Interactive mode for ArgumentParser
- Quick navigation in Swift-DocC websites
- Kafka client package for Swift
To shine a light on their exceptional work and to inspire future participants, let’s take a closer look at their accomplishments.
Table of Contents
Bootstrapping SwiftSyntaxBuilder
Author: Fredrik Wieczerkowski
Mentor: Alex Hoppen
The SwiftSyntax
library, which lets users represent, parse, and generate Swift source code using Swift, received major updates. The result builder-based SwiftSyntaxBuilder
Domain-Specific Language (DSL) was improved and inconveniences in the API surface were fixed and thoroughly tested.
During this process, the templates that generated part of SwiftSyntaxBuilder
’s sources were ported from gyb
to type-safe Swift code based on SwiftSyntaxBuilder
. In other words, the library now uses itself to generate its own code!
The result of this bootstrapping process is a more robust and ergonomic API for generating Swift code.
Here’s an example of how Swift’s expressiveness allows for builder closures which closely resemble the generated source code. For the following snippet of Swift code:
struct Point {
let x: Int
let y: Int
}
The corresponding SwiftSyntaxBuilder
DSL would be:
StructDecl(identifier: "Point") {
VariableDecl(.let, name: "x", type: "Int")
VariableDecl(.let, name: "y", type: "Int")
}
For more information, check out the project’s writeup.
Improving the debug output of the type inference algorithm
Author: Amritpan Kaur
Mentor: Pavel Yaskevich
Swift’s type inference algorithm is at the heart of the Swift developer experience. It is the algorithm that allows us to write source code without always providing explicit type information to the compiler.
The algorithm is implemented via a constraint-based type checker that gathers available type context from the source code and attempts to solve for a concrete type for those parts of the source code that are missing type information, resulting in a valid, fully-typed Swift expression.
The type inference algorithm produces an output for debugging that is especially helpful when working with invalid expressions. However, this output has been difficult to understand and unwieldy:
$T0 [lvalue allowed] [noescape allowed] delayed bindings={} @ locator@0x1258ee200 [OverloadedDeclRef@/…]
$T1 [noescape allowed] delayed literal=3 bindings={} @ locator@0x1258f0ac0 [IntegerLiteral@/…]
$T2 [noescape allowed] delayed literal=3 bindings={} @ locator@0x1258f0b78 [StringLiteral@/…]
$T3 [noescape allowed] delayed bindings={} @ locator@0x1258f0c40 [Binary@/… -> function result]
This output has repetitive elements, seems disjointed, and does not show essential type and process details.
Over the last few months, debug output was reworked and reformatted the output to improve its readability. To make it easier to follow how an expression or subexpression is type checked, the output now closely tracks constraint solver steps by showing constraint simplification and solver scope changes. The output also explicitly states important type information in context to show how the constraint solver’s path changes type variable bindings and relationships. Additionally, a redesigned layout groups together type properties, simplification process, and nested constraint solver scopes for a more visually friendly format:
$T0 [allows bindings to: lvalue, noescape] [attributes: delayed] [with possible bindings: <empty>]) @ locator@0x13ca3e400 [OverloadedDeclRef@/…]
$T1 [allows bindings to: noescape] ($T1 [attributes: delayed, [literal: integer]] [with possible bindings: (default type of literal) Int]) @ locator@0x13ca3f398 [IntegerLiteral@/…]
$T2 [allows bindings to: noescape] [attributes: delayed, [literal: string]] [with possible bindings: (default type of literal) String]) @ locator@0x13ca42e68 [StringLiteral@/…]
$T3 [allows bindings to: noescape] [attributes: delayed] [with possible bindings: <empty>]) @ locator@0x13ca42f30 [Binary@/… -> function result]
Check out Amritpan’s forum post for more information.
Interactive mode for ArgumentParser
swift-argument-parser provides a fast and easy way to create high-quality, user-friendly command-line tools in Swift.
The upcoming interactive mode can prompt for missing inputs to help guide users through unfamiliar command line tools. The interactive mode continues ArgumentParser’s approach of providing a lightweight coding experience, building on the metadata tool authors already provide.
$ roll --help
USAGE: roll --times <n> --sides <m> [--verbose]
OPTIONS:
--times <n> Rolls the dice <n> times.
--sides <m> Rolls an <m>-sided dice.
-v, --verbose Show all roll results.
-h, --help Show help information.
$ roll --verbose --times 3
? Please enter 'sides': 6
Roll 1: 1
Roll 2: 6
Roll 3: 3
Total: 10
Interactive mode will be included in a future ArgumentParser release, but you can try it out now on the feature/interactive
branch.
Quick navigation in Swift-DocC websites
Author: Sofía Rodríguez
Mentors: Marina Aísa, Franklin Schrans, Beatriz Magalhaes
This feature creates a fast and accessible way to navigate and discover symbols in Swift-DocC documentation websites, similar to Open Quickly in Xcode.
Key features
- Fuzzy search – Find symbol names through an fuzzy match of search terms.
- Symbol ranking – Results are sorted so the most relevant ones are at the top. This is done using similarity metrics such as the length difference between the input and the match, the length of the symbol name, and how far from the beginning of the string the match occurs.
- Keyboard navigation – This feature is fully accessible via keyboard shortcuts, making it easy to perform queries even when the navigation sidebar is collapsed.
Check out Sofía’s forum post for more information.
Kafka client package for Swift
Author: Felix Schlegel
Mentor: Franz Busch
SwiftKafka is a new Swift package that provides a convenient way to communicate with Apache Kafka servers. During the package development, the main goal was to create an API that leverages Swift’s new concurrency features. Under the hood, this package uses the librdkafka C library, wrapping the unsafe and blocking APIs into safer, more ergonomic Swift APIs.
Here are examples showing how to use the new package to produce and consume messages from a Kafka server:
Producer API
The sendAsync(_:)
method of KafkaProducer
returns a message-id
that can be used to identify the corresponding acknowledgement. Acknowledgements are received through the acknowledgements
AsyncSequence
. Each acknowledgement indicates that producing a message was either successful or returns an error.
let producer = try await KafkaProducer(
config: .init(),
logger: logger
)
let messageID = try await producer.sendAsync(
KafkaProducerMessage(topic: "topic-name", value: "Hello, World!")
)
for await acknowledgement in producer.acknowledgements {
/// ...
}
Consumer API
After initializing the KafkaConsumer
with a topic-partition pair, messages can be consumed using the messages
AsyncSequence
.
let consumer = try KafkaConsumer(
topic: "topic-name",
partition: KafkaPartition(rawValue: 0),
config: .init(),
logger: logger
)
for await messageResult in consumer.messages {
// ...
}
Wrap up
For more information, as well as previous year’s projects, check out:
Leave a Reply