[Pre-pitch] SwiftPM Manifest based on Result Builders

I think the sentiment is is more towards solely ResultBuilders DSL than other changes. These changes could be proposed separately though. Discussion on a single aspect may neglect other proposed changes that are less controversial.

2 Likes

afaict it is true that Result Builders auto complete support in Xcode is not perfect yet. That said, there is nothing inherent to such API which prevents it from becoming great, so its more of tooling catching up.

having worked with and on both APIs, the Result Builders API suggested here with it's strongly typed approach (for modules as one example) is far simpler to type and get right than the polymorphic initializers the existing API offers.

cc @rintaro

2 Likes

being able to rely on constant evaluation and the type system instead of executing the manifest to produce a JSON static representation would be amazing for reducing security surface area. looking forward to hearing more on what is possible to that end.

4 Likes

Which platforms use a security sandbox? (A few years ago it was macOS only.)


Should the add-product and add-target subcommands (from SE-0301) be revised?

It sounds like that piece should in fact be broken out into its own proposal then.

1 Like

:100:

Agreed, but you've missed my point. With gigantic initialisers, people -- especially those learning SwiftPM -- at least had a "handrail" to climb the staircase of defining their package. One simply fills in the parameters. With result builders, the handrail is removed but the staircase remains. One would expect the tooling to be more helpful for this use case but it isn't, so people will be more inclined to copy and paste from elsewhere.

Say someone opens a package definition:

Package {
    // Insertion cursor blinks.
    // What goes here?
    // Modules? Dependencies? Targets? Libraries?
    // How does SwiftPM expect me to model stuff?
    // Guess I better read the headers or 
    // copy and paste examples from documentation 🤞...
}

To my mind, this is a usability regression because the interface and the lack of tooling make modelling a package quite mysterious. Perhaps, in the absence of tooling, the template packages could come with commented out examples, to help clear the mystery?

Module might also be a bit too technical of a concept for many people, too, despite it being completely correct. People tend to think of modules as apps, tools, plugins, frameworks, libraries, tests etc.

I wonder if import would be better, since it matches the inter-module relationships people are familiar with in the source code? Or importable, in case the compiler can optimise out unused dependencies?

I've been keeping up with the thread and overall I'm very positive with this change. I think that having a DSL for SPM while keeping the power of being Swift code is very powerful and desirable (is one of the best things about cocoapods).

I agree with Kyle here. I wouldn't like to see this as a normal @main script. I don't see the point really, it just adds verbosity for no gain. We should be able to just define a top level Package and that should be the entry point (even better if it turns out you don't even have to run it, which seems a very interesting approach).

I hear the concerns about discovery but I'm not sure how big a problem it is. Realistically I barely write a package file from scratch. Mostly I start with the generated template and move or copy paste things around. Most of what I endup doing is adding a min platform req, and it took me a while to know the correct order, and autocompletion is not very helpful there. So I don't think the difference of discovery with this change is that big tbh.

But there is one thing that has me VERY intrigued:

Is this really true? How is this a tooling issue and not a language issue. AFAIK there is no way of restricting a type construction into a scope of a result builder (for example that Internal/External types only make sense inside an include closure). So the language won't tell the tooling what types are more appropriate inside the DSL, unless something is hardcoded somehow. I'm asking because I often find this difference when comparing APIs with Kotlin, mainly because they have a small functionality with tons of potential, what on this forums has been called in the past "self rebinding". I don't want to derail the conversation to that, but I wonder what the tooling can do to solve this if the model of the language doesn't offer how to describe it.

3 Likes

Just to be clear, I’m all for the result builder approach. I think the regression in the UX should be clear so an adequate compensation can be made. After all, we’ve figured out how to build apps in SwiftUI, even if it was through some measure of copying, pasting and tweaking until an intuition about the APIs was developed.

1 Like

You are right that the current language doesn't have a way to express like "you can only use these certain types (e.g. Internal and External) in this context". So there's no way to limit types that appear in code completion depending on the context(, nor should we do so). All we can do is prioritize items that have the right type (e.g. that conforms to Dependecy protocol).

In a result builder context, the builder type can provide the contextual type to elements in its context. For example:

protocol Animal {}
struct Lion: Animal {}
struct Penguin: Animal {}

struct Zoo {
  @resultBuilder
  struct Builder {
    static func buildBlock(_ animals: Animal...) -> [Animal] { animals }
  }

  init(@Builder _ body: () -> [Animal]) {}
}

let _ = Zoo {
  Lion()
  <HERE>
}

We should be able to know that the contextual type is Animal because Builder.buildBlock(_:) only accept Animal. So code completion should be able to prioritize types that conform to Animal protocol, that is Lion and Penguin. (But we should/can not stop suggesting other types/functions/variables because they might have properties/methods that returns an Animal type.)

7 Likes

That being said, there are possible improvements here. Like, for example, when you manually invoke code completion (with ESC, Ctrl + space, or Ctrl + .) without filtering text, it only shows items with matching types, so you can choose from the UI. As soon as you type something, it shows normal completion items filtered by the input characters.

It's just an idea, I haven't really thought it through, so I don't know if this is a good idea or not.

1 Like

To me, investing time in meeting this decision when there're tons of bugs and existing radars, with a poorly tool integration, it is literally a waste of time and resources to be honest.

2 Likes

+1 for the main approach. Results builder sounds good too. I do think that if folks want to declare all configurations in a config file (json/yaml) then hopefully they can still do this adhoc.

I’m not sure that’s completely fair. Over at the Compile Time Constants pitch, this work could be a step towards improving the performance of SwiftPM:

So maybe with less sandboxes, we might get some of that two-three minutes our Macs spend stuttering playing music, switching Slack channels or browsing web pages while trying resolving 30 or so package dependencies after a changing a branch :grimacing::crossed_fingers:t2:

Bugs > Performance

Doesn't matter that you have the fastest tool if it is unusable because of blocking bugs.
Just my opinion

I'm curious to know what issues you are referring to, and if they're more on SwiftPM itself or its integration with Xcode.

In my experience, all my issues with SwiftPM are in its integration with Xcode, so the unfortunate reality is that even if those get fixed within 24 hours of being reported... they're effectively not fixed for months at a time while we wait for the next version of Xcode to be released.

And in some cases, we have to wait entirely for the new major version of Xcode, leaving bugs existing for an entire year - and then some, as you have to then make sure you're able to build with the new version of Xcode.

1 Like

bundle_resource_accessor, which I believe is generated by Xcode is wrongly generated for example. Boy bear in mind that 95% of SwiftPM relies on Xcode Integration, where is mostly used into, so one problem links to another.

Only two-three minutes? You must have fast internet.

Why does it download the same dependencies time after time even though they haven't changed in months, whenever I change branches.

Why do I have to force quit Xcode if I try to close a project while it's Resolving package graphs?

This is off-topic really but I have terrible internet and SwiftPM's caching is helping me tremendously:

❯ rm -rf .build/
❯ swift build
Fetching https://github.com/crossroadlabs/Regex.git from cache
Fetching https://github.com/mxcl/PromiseKit from cache
Fetching https://github.com/kylef/Commander.git from cache
Fetched https://github.com/crossroadlabs/Regex.git (1.13s)
Fetching https://github.com/kylef/Spectre.git from cache
Fetched https://github.com/kylef/Commander.git (1.14s)
...

I'd like to hug everyone who's helped ship this :)

To make this somewhat on-topic: I don't think arguing this change is without merit because other things are imperfect is fair to the proposal, and I doubt it's helpful to the authors. It's not like this pre-pitch or its eventual implementation would fundamentally prevent those other issues from being addressed.

4 Likes

It seems that Xcode use a private SwiftPM framework, not build upon on the open source swift-package-manager. Their function is the same and share almost the same name but they are different thing. See [Optimus] Simplify resource_bundle_accessor.swift by Kyle-Ye · Pull Request #3824 · apple/swift-package-manager · GitHub and [SR-15550] Loading a resource from a bundle fails due to incorrect bundle path - Swift

That's why I insist on that making progress on spm as a standalone product without taking in mind that it is an apple tool and more importantly, a tool that is widely used in xcode because is our unique IDE is just a fairy tale.
More cross-team and collaboration with the xcode team (for example integrating SPM as a plug-in in Xcode would've been great). But the effort is pushing because it is what it sells more.

3 Likes
Terms of Service

Privacy Policy

Cookie Policy