Declarative package description for SwiftPM using Function Builders

Motivation

Function builders allow us to have declarative interface. Declarative interface also gives you simpler and less source code. SwiftUI is a good example. It allows you to have declarative syntax to build view hierarchy very easily.

Currently, SwiftPM package description provides static func to build the description, but we might have alternative way using function builder here.

Function Builders

Below is an example of current package description. It provides convenient static func, so I think it's good enough interface.

let package = Package(
    name: "MyLibrary",
    platforms: [
        .macOS(.v10_14),
    ],
    products: [
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
    ],
    dependencies: [
        .package(url: "https://url/of/another/package/named/Utility", from: "1.0.0"),
    ],
    targets: [
        .target(name: "MyLibrary", dependencies: ["Utility"]),
        .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
    ]
)

Below is an example of new approach using function builder. It can be more declarative than current one.

let package = Package(name: "Paper") {
    ProductList {
        Product(name: "tool", type: .static) {
            TargetList {
                Target(name: "tool")
            }
        }
    }
    DependencyList {
        Dependency(
            url: "http://example.com.com/ExamplePackage/ExamplePackage",
            from: "1.2.3"
        )
        Dependency(url: "dev", exact: "1.2.3")
    }
    TargetList {
        Target(name: "tool") {
            TargetDependencyList {
                TargetDependency(name: "Paper")
                TargetDependency(name: "ExamplePackage")
            }
        }
        Target(name: "Paper") {
            TargetDependencyList {
                TargetDependency(name: "Basic")
                Target(name: "Utility")
                Product(name: "AnotherExamplePackage")
            }
        }
    }
}

However, I think this approach has some downsides. So I would like to hear your opinions.

Pros

  • Declarative description

Cons

  • Can't have type constraints for all of description contents unless we defined function builder for specific types
  • Compiler error is not friendly when we have wrong content at unexpected place

The only concern I have for function builder is that it might not work with package description well because it has type constraints in the tree structure. For example, it's not allowed to have Target on top level. We need to think if it should get compiler error or be ignored for encoded data.

Data mapping

As long as each description content conforms to Encodable, it can be mapped with current data types.

Here is my rough implementation of the functionality. With this, we don't need to modify current data types.

5 Likes

I'm -0.5 on this; for me the benefits do not outweigh the drawbacks. In addition to the stated cons, you'd lose context necessary for autocomplete to give good suggestions which hurts API discoverability. Also FWIW the existing package description API is already "declarative".

5 Likes

We should wait, function builder is not even part of the language yet.

3 Likes

Thank you for your opinion!
I figured out that true benefit of function builders is define-and-run idea which means all of different types can be treated as if it's same at compile time, but they behave differently at run time. Declarative interface is not the biggest motivation to be used.
I don't see the need in SwiftPM description, so I'm kind of thumbs down now...