@main: Type-Based Program Execution

Notably Swift already has this feature: the two @UIApplicationMain and @NSApplicationMain attributes. The feature is already there and it's not going away. This seems reason enough to formalize and generalize it.

Additionally, while I understand the complaint that it only saves one line of code and a single file, reducing boilerplate is a good goal for initial tasks when it comes to getting people up on new libraries/products.

Eg. consider a Swift framework for deploying code to an IoT device that has a special program loader, being able to document “simply add @main to your App: IOTApp struct is the kind of documentation that will reduce the “trying it out” friction for that framework, thus helping it gain popularity and thus leading to a general perception that Swift is a viable choice across domains.†

Reducing experimentation friction is important for adoption. For me this is a feature that does that, it is not about how little it effects people already using Swift for existing codebases.

† versus, create a new file main.swift, add boilerplate X (varied based on your App struct’s name), ensure main.swift is in your executable target in your Package.swift.‡

‡ As a community perhaps we should look at what such tools are doing, eg. Vapor comes with a command to generate and execute Vapor projects, features like this might alleviate that need. The command IMO, is there to reduce initial friction, but makes Vapor seem like a black box whom you do not know how it works.

14 Likes

IMHO, Apple framework specific attributes should not be taken into account when choosing if a feature is worth adding to Swift or not.

3 Likes

I can sort of agree in general, but all programs need an entry point. Since attributes of this type already exist for Apple platforms, and they provide functionality that all programs need in some form, it makes sense to take what's there and generalize it.

1 Like

The proposal does state that new forms of main could be supported. I think we should continue to support the raw (argc, argv) -> int forms for Unixy platforms too.

I too had originally envisioned this as a wrapper-style feature, but I think the way Nate's proposed it is just as expressive. The framework-controlled main implementation can come from an extension to a protocol provided by the framework. For instance, @UIApplicationMain's existing custom behavior could be replaced by adding a main method to the UIApplicationDelegate protocol in UIKit.

2 Likes

I like this idea:

  • No invisible quasiprotocol
  • No hidden relationship between type-level annotation and specially blessed method; annotation sits right at the point of interest

And it leads to the interesting possibility of having multiple entry points:

struct FancyStuff {
    @main
    func commandLine(args: [String]) { … }

    @main 
    func gui() { … }

    @main 
    func adminUI() { … }
}

An attribute on the annotation could identify the default entry point for command-line execution.

2 Likes

One big difference is that with a main protocol requirement users could provide a custom implementation, even if that is not recommended. Is that what we want here?

A wrapper solution along the lines that @michelf suggested would avoid that. At a glance, it looks like it could fit in nicely alongside things like property wrappers and function builders.

How would this replace @UIApplicationMain?

You can't have multiple entry points for a single app. The OS has to know what function to call. On UNIX, this is usually the _start function, that is generated by the toolchain and call main().

On Apple OSes, this is main() which is called directly by the dynamic linker (but the name probably doesn't matter as dyld uses the LC_MAIN header to find it).

On Windows, this is defined by an header in the binary image, but it also generated at compile time by the toolchain.

To support multiple entrypoint, you would need to have a framework that support it and call the appropriate functions from the OS expected entrypoint. Which bring us to case one, as your code would have to call that entrypoint explicitly.

2 Likes

Hi Nate,

I'm generally +1 on an attribute that achieves something like this, but I don't think this gives much value as proposed. This appears to be a more magic way of writing YourType.main() as top level code. This syntactic sugar seems to increase boilerplate by requiring a static method to be declared :-(

Beyond that, there is a conceptual mismatch with the way that @UIApplicationMain and friends work. These attributes have special behavior (w.r.t. argument passing) that are specific to the classes they are tightly coupled with, which allows them to provide slightly more value than calling main().

My recommendation would be to more closely follow the precedent set by @UIApplicationMain and @NSApplicationMain: we should have command-line-script-specific attributes that are tightly coupled to the types being used (allowing them to provide more value and eliminate boilerplate instead of moving it around).

Now of course, building more hard coded attributes into the compiler for specific frameworks isn't particularly appealing. What if we took a note from the property wrappers feature and introduced a 'type wrapper' concept that allowed pulling all of these out into the library?

-Chris

10 Likes

+1. If we could generalize to a type wrapper feature that is relevant to other use cases that would be awesome!

2 Likes

Given this and some other comments, I think the proposal could be clearer that I don’t expect the author of an app to ever write a static main method. The intent here is for libraries and frameworks to provide the main() method, not client code — as the author of an app, all you would ever need to do is apply the @main attribute to an appropriate type.

2 Likes

And we have two approaches for this here. One where the main method is provided through inheritance (or an extension of a protocol); the other where it is provided through composition (with a custom type as an attribute).

The later approach it is more flexible, I believe. I could for instance create my own wrapper that validates a Mac App Store receipt before launching the app, and use it like this in all my apps:

@MASReceiptValidatingApplicationMain
class AppDelegate: NSApplicationDelegate { ... }

I bet a lot of Mac apps are using a custom Main.swift for exactly that reason.

1 Like

Hi @Chris_Lattner3,

I like your proposal better than mine, simply because yours has a nice nomenclature to go with it :slight_smile: This is exactly what I was suggesting as well - a type wrapper that puts the construction of the type and the method invocation (and its requirements) into the library rather than the compiler.

It seems to me like this proposal in its current form can already subsume @NSApplicationMain and friends. It already allows for framework-abstracted main implementations, because the method can come from a framework-provided extension. The main differences in taking a "wrapper" approach I can see are:

  • As @anandabits noted, a "wrapper" design would prohibit the annotated concrete type from shadowing a main that came from a protocol extension.
  • Putting the main implementation in a dedicated wrapper type would mean that it doesn't have to pollute the namespace of types conforming to the main-providing protocol.
  • A "wrapper" design would lead to multiple framework-specific attributes instead of one @main attribute that works for any eligible type.

(To be clear, I'm not making a judgment about these differences, only trying to enumerate the practical effects.)

5 Likes

Am I being stupid, or does this proposal misunderstand how @UIApplicationMain works?

In a current project, I have a file called AppDelegate.swift that looks like this:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    …
}

Note that the static main function is not provided by class AppDelegate, so there's nothing to link the special behavior to whatever actually provides main other than the semantics of UIApplicationDelegate.

So, as compared to the above, to what type would you apply the @main attribute? According to your pitch, it can't be applied to AppDelegate, because that would force AppDelegate to implement the static function.

The idea is that UIKit could provide main in an extension:

extension UIApplicationDelegate {
  static func main() {
    UIApplicationMain(Process.argc, Process.unsafeArgv, nil, _typeName(Self.self))
  }
}

and then applying @main to your class that conforms to UIApplicationDelegate will cause the program entry point to be generated from the main method it inherited from the protocol.

3 Likes

Right — you can try this out today, sort of:

  1. Comment out the @UIApplicationMain attribute you get from Xcode's template (if this proposal were accepted, you would replace it with @main)
  2. Add the extension from Joe's post (this could in theory be added to the UIKit framework)
  3. Add a main.swift file that just calls AppDelegate.main() (this would be auto-generated by the compiler)
4 Likes

Yes, it took a bit of thinking, but, I agree that the current proposal could subsume @NSApplicationMain and @UIApplicationMain.

Yes, those are the differences that I see also. The differences do seem interesting to me - particularly the ability for a framework to provide its own definition and semantics of an attribute to indicate entry.

The shadowing prevention actually makes it clearer that the user is not expected to write this IMO, but perhaps having that flexibility is useful. I feel like I am missing something about this point though - I don't immediately see the use case the shadowing, could you (or @anandabits) help fill in my blind spot here please?

I don't think there's a use case per se, it's just something that would fall out of the feature as currently proposed, since you'd be allowed to write something like:

@main class Foo: UIApplicationDelegate {
  // This would shadow the UIApplicationDelegate extension method main
  static func main() {
    print("Entering the real main")

    // Invoke the extension method
    (self as UIApplicationDelegate.Type).main()
  }
}
1 Like

Yes, but:

  1. Why would it be logical to implement it in the UIApplicationDelegate protocol? That's for delegate behavior, and it's a fairly long way round to think of it as a delegate responsibility to provide the function. At best, it perpetuates the unfortunate history of random startup-related stuff being jammed into the app delegate, whether it makes sense or not.
  2. It's not customizable, since you couldn't override the UIApplicationDelegate default method.
  3. The proposed behavior would also prevent you from adopting a main function from a library you didn't write.

Maybe it would work if the syntax was @main(SomeTypeName), where SomeTypeName implements the static main function. That would let you choose between competing implementations, and in the "normal" case, it would presumably be something like @main(UIApplication) — although I don't think main is currently declared inside any actual Swift type, but I guess it could be, or the compiler could pretend that it was.

Any of the proposals in this thread would involve the compiler promoting one method of one type to be the OS-level mail/start method. That promotion could presumably happen even in the presence of multiple candidate types via a compiler switch; it could also happen for multiple individual methods.