38 comments

[ 4.6 ms ] story [ 84.7 ms ] thread
There’s some really interesting work coming out of the Swift project and smart people involved in it’s development but I keep feeling like not enough attention is being paid to basics. Tooling and compiler performance need some serious attention. These are boring tasks but crucial for the language’s wider appeal.
The linked project is nothing but tooling and compiler work. It’s hard to understand your comment in that context.
Both of these things can be true at the same time. This is a chunk of necessary work and not enough of that kind of work is being done.
Obviously, but you saying that doesn’t explain what the person I was replying to had in mind. Their comment doesn’t read that way.
That's not true in even a specious way. You left out "performance" and then shot down a strawman by claiming an independent parsing library improves compiler performance.
I don’t think you understand what the project is.

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.

You can repeatedly condescend to everyone in this thread, yet, we all continue to grok that having a plan of goals for improvements that would impact both tooling and compiler, when the plan it has to take over parsing throughout the compiler succeeds, is not the same as focusing on compiler performance.

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.

> by claiming an independent parsing library improves compiler performance.

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.

Swift 5.4 and 5.5 made some major improvements to incremental builds (rebuilding fewer files after a change) and pretty much every release since 5.1 or so improved full module rebuild performance (doing less work for a file that actually has to be rebuild). But it can be hard to perceive small cumulative improvements when everyone’s codebase keeps getting larger as well.
Exciting stuff.

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.

1: https://github.com/alchemy-swift/alchemy

How does Alchemy compare to Vapor?
Oh I think Vapor is super powerful, feature packed, and the team has done an amazing job building up and maintaining over the past few years, especially when swift on the server was just getting on it’s legs.

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

As someone who’s day job is Laravel it’s cool to see that this stuff can be implemented safely and performantly in a strongly typed language.
I don’t mean to minimize your work (it’s excellent and I always appreciate folks who work on open source), but what makes me wary is the lack of much support: maintenance, Kafka, Database integration wrappers, and so on. Given that I’d much rather go with Vapor, since there is tons of community support there.
Any particular integration you're looking for? Out of the box it supports MySQL & Postgres but it's fully compatible with any external libs built on SwiftNIO, which is the de facto foundation for most Swift server projects.
"New thing not perfect!"
How is the performance? Vapor's is terrible, which combined with the small ecosystem makes it a bad choice for backend.
Is this the reason IBM stopped its Swift on the server endeavors?
Possibly one of them. Swift has just generally had a confusing, lopsided development cycle that has made it extremely hard to build larger/more-abstracted systems on top of it. I'm not quite so sure what breakage looks like these days, but the language only got null-checking a few months ago. I do hope they lay out more groundwork before making the push to non-Apple platforms.
> the language only got null-checking a few months ago.

???

> but the language only got null-checking a few months ago

Are we talking about the same language?

out of curiosity what does terrible means relative to other web server frameworks, and any idea what explains it?

I would expect a Swift web framework has at least the potential to be performant

"terrible" here means within the ballark of Express and usually lower than Phoenix (the Elixir web framework), far slower than Go, and than most Java framework. The source is the TechEmpower framework benchmarks, with the caveat that Vapor isn't in all the categories, and the usual benchmarks caveat.

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.

> From what I've heard, Swift was also optimized for low memory usage rather than raw speed

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

Thank you for the explanation
"If it represents authored Swift source, then the data representing it is immutable. You don't change what an author wrote without their permission and initiation."

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...

No; the first sentence is the dominating one. If the data represents Swift code, then it's immutable. "Permission" here would mean presenting a UI to the user of a tool asking them if some source transformation should be performed.
I think the interpretation intended there is that if you hand a representation of a file to a tool, it needs to be immutable.

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.

ok thanks ! got confused with the meaning of "authored"
I want to take the chance while Swift is on the front-page to talk about something mind-blowingly exciting (to me, at least) about it that people might not be aware of:

  >> 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)

I share your enthusiasm for C++ interoperability. I'm a computer graphics researcher and I have written my share of C++. I'm using Swift on my current project and there are a wealth of C++ geometry and numerical libraries that I would love to integrate without having to jump through Objective-C or an automatic binding generator. Swift is a very pleasant language to work in compared to C++ (YMMV), so being free to pull in C++ libraries natively might make it attractive to a lot of folks.
This might be a foolish question — but why not write this in Swift?
The whole compiler and the vast majority of the runtime are in C++. This module is intended to integrate with the compiler, and uses the compiler's own parsing code. That said, there are Swift bindings available as a separate library: https://github.com/apple/swift-syntax
> # Ignore the following admonition; it applies to the resulting .cpp file only

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.

I'm not sure I understand the significance of this library.

Can it help implement something like a Visual Studio Code Language Server?

I was really hoping I could use it to implement some complex automated refactoring of a pretty large swift codebase (mainly from completion handlers into async/await) but alas, the docs on "Syntax Rewriters" is just a "TODO".

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.)