Swift Syntax and Structured Editing Library
github.com
github.com
With async/await landing last week and the leaps and bounds of the package manager over the last couple years, the ergonomics of using Swift for non-iOS work has gotten significantly better. Coupled with the built in safety and elegance of the language I think that, especially for server work, Swift is a very attractive option.
Shameless plug; for these reasons I built a Swift backend framework for fullstack iOS development[1] that we've been using it at my company while going through YC this summer.
It's been very powerful to have mobile and server in the same language and codebase and has helped us (native iOS backgrounds), move quickly.
That being said, after building with Vapor I felt like there was room for a framework that focused a lot more on convention like Laravel / Rails to keep things lean and fast.
Consider ORM APIs…
In Vapor require property wrappers around each field, as well as manually entering in the table name, db column the keys map to, and needing to write an initializer.
final class Planet: Model {
// Name of the table or collection.
static let schema = "planets"
// Unique identifier for this Planet.
@ID(key: .id)
var id: UUID?
// The Planet's name.
@Field(key: "name")
var name: String
// Creates a new, empty Planet.
init() { }
// Creates a new Planet with all properties set.
init(id: UUID? = nil, name: String) {
self.id = id
self.name = name
}
}
In Alchemy the table name is inferred to be the type name pluralized, and each db column is inferred from the Swift property name (even mapping camelCase to snakeCase by default). It also supports structs, and thus the automatic initializers.And because it uses Swift.Codable doesn’t have to use expensive reflection APIs to decode, giving a decent performance boost (disclaimer: last time I checked).
struct Planet: Model {
var id: UUID?
var name: String
}
Similarly with relationships, Vapor requires somewhat verbose string / key path arguments in the property wrappers and supports 1-M M-1 & M-M relationships. final class Star: Model {
@Children(for: \.$star) // 1-M
var planets: [Planet]
@Parent(key: "star_id") // M-1
var star: Star
}
vs inferring the type and connecting keys, as well as supporting customizing the relationship behavior in Alchemy for things like HasManyThrough, BelongsToThrough, etc. All inferred keys are, of course, overridable in case your db naming convention is different. struct Star: Model {
@BelongsTo var star: Star // M-1
@HasMany var planets: [Planet] // 1-M
@HasMany var moons: [Moon] // “Has Many Through”
static func mapRelations(_ mapper: RelationshipMapper<User>) {
// infers mapping from stars.id -> plants.star_id -> planets.id -> moons.planet_id
mapper.config(\.$moons).through(“planets”)
}
}
There’s also a bunch more examples of APIs around QueryBuilder, Caching, Jobs, Queues, Redis, Commands, Authentication, Middleware etc[1]. And everything will be converted to async/await in the coming days.1: https://github.com/alchemy-swift/alchemy/tree/main/Docs#docs
???
Are we talking about the same language?
I would expect a Swift web framework has at least the potential to be performant
For why it's not performant, I don't know precisely. Part of it may be because people don't use it a lot on the server side so it's not as optimized as the others. From what I've heard, Swift was also optimized for low memory usage rather than raw speed. Outside of that, I don't know. I would expect it to be around Go.
at a 1000 foot view, Swift inherits its reference counting system from Objective-C, meaning objects are freed immediately when their retain count goes to zero; vs a tracing GC releasing many objects whenever a tracing cycle runs. maintaining reference counts also adds a cost that a different GC approach could avoid. it's true that Swift will maintain a lower memory footprint, potentially at the cost of lower throughput
I would be a little surprised if that explains the bulk of a performance difference though, vs Swift frameworks being newer and less optimized
It’s not an independent library. The plan is for it to take over parsing throughout the compiler. Numerous performance improvements that would impact both tooling and the compiler are listed as goals.
I hope this is a silver bullet that somehow address all of this and succeeds, but 7 years of Swift in, I've seen plenty of plans of goals that could do things if other ambitious plans succeed.
The library is not independent, and one of the goals is to improve compiler performance.
You wrote a criticism of me of me based on your own misunderstanding of the project, in response to a comment that wasn’t addressed to you.
Leaving aside that the personal attack was pointless in the first place, it’s not obvious what value there is in trying to defend it.
I wonder why the tool doesn't insert the admonition automatically? Then it would never get forgetten, and no disclaimer would be necessary in the actual source.
I've also found a time/date stamp to come in handy more than once.
Can it help implement something like a Visual Studio Code Language Server?
I think this is an area where software engineering is severely lacking: the ability to "code your code", but with something more advanced than regexes. Why can't I do something like this pseudocode:
functions
.where(params.last.isClosure)
.where(returnType: Void.self)
.forEach { f in
f.setReturnType(f.params.last?.asClosure.paramsTuple)
f.setAsync(true)
f.setThrows()
}
to take functions which take a completion handler and convert them to being async? (I know it's a bunch more work than that but I feel like, even if the resulting code doesn't compile, I can fix up the resulting issues faster than having to do all of this myself.)This library is intended for syntactic tools: formatters, highlighters, that kind of thing.
I'm sorry, what ?? Did i read this correctly ? Are they representing code using either mutable or immutable structures depending on whether the sourcecode has copyright or not ?
I'm really confused...
The representation likely is full of string refs and pointers to the actual data in memory to prevent egregious copying, and the caller of the api should not be able to modify the data structure they receive and accidentally modify the actual file unless they do so through an api intended for it.
Nothing to do with copyright, more that swift-format or your lsp integration should never accidentally modify a file without user initiation.
>> Swift has support for C++ interop, without writing manual bindings! <<
Yes, you read that right. Swift can interop with and import C++ members/values, without needing to write a corresponding Swift type mapping. It's currently the only language I'm aware of that can do this (Nim doesn't count). I think it's because Swift is so integrated into LLVM/clang, that it can do a sort of mixed-compilation of Swift/CXX sources. Such that from a compiler's perspective which language they come from doesn't matter -- but I have no real clue.It's very early, but it can already do some impressive things. The "cxx-interop" tag on the Swift Forums will show you what is basically a timeline of it's development and current status:
https://forums.swift.org/tag/cxx-interop
And here is the "C++ Interopability Manifesto", the outline they have for the feature in the language: https://github.com/apple/swift/blob/main/docs/CppInteroperabilityManifesto.md
For a real-world example of using this, here's a test repo from the core developers: https://github.com/plotfi/cxx-interop-test
https://github.com/plotfi/cxx-interop-test/blob/main/Sources/CXX/include/CXX.h
https://github.com/plotfi/cxx-interop-test/blob/main/Sources/Swift/main.swift
You see here direct import and use of "std::" namespace, including "std::vector" and "std::string_view", as well as use of structs and arrays: import CXX // <-- This is CXX.h, local C++ file
import std.string_view
import std.vector
"42".withCString { cstring in
let view = std.__1.string_view(cstring)
assert(is_str_42(view))
}
var MS = MyStruct()
var MS1 = MS[0]
var v = V()
let arr = v.getVec()[0].swift_array()
print("std::vector: \(arr)")
Fucking mindblowing! It requires a ".modulemap" for the C++ code, but that can be auto-generated with most build tools AFAIK.This is one of the most exciting things happening in tech for me. With Swift supporting Windows now, you have a great language for consuming C/C++ libraries and publishing native binaries or shared libs. Really looking forward to the future developments here.
I have never even written Swift, but I've been following the developments here closely. After Swift 5.5 features, looking like a really solid language if you want to do systems programming but can sacrifice some binary size and performance for a higher level language. Particularly the new Actors stuff for safe concurrency.
(Also, there's a chance Kotlin Native may get C++ support in the future, which would also be sweet)