Introducing FoundationLite framework as a portable subset of Foundation

There is a difference between retaining the syntax and being fought by the community because you want to use it. I have often heard here that oop sucks, is dead and inferior to value types and functional programming. That's not a clear commitment to oop in Swift.

A clear commitment by the core team that oop is a equal part of Swift would enable to point at the statement when people start with the oop hate.

I fear I'm not good at putting in words what I intend to say so of something is unclear just ask for clarification.

Full agreement.

There is no silver bullet, different languages prioritise different things. For example, the fact that fatal errors are unrecoverable is a big problem for server-based applications (you need to build some infrastructure around it so that an illegal array access or integer overflow doesn't just halt your entire service), while it probably makes a lot more sense for iOS/macOS (the user can just restart the App). Similarly, the choice of ARC vs. GC is not "superior" but a clear tradeoff between predictable performance and not requiring users to deal with low-level details such as retain cycles; one which, I think, makes much more sense for certain types of applications than for others.


A young language, just like a political party, becomes the surface onto which people with very different desires, needs and philosophies project their ideas. We're already seeing this with some people wanting to push traditional (Java-like) OOP, asking for things like abstract classes, a protected modifier, etc., whereas others try to push FP and would like to see HKTs and so on; yet other people seem to want Swift to be much closer to the metal and want to implement parts of Rust's borrow checker.

Ultimately, I think, the language will have to decide what it wants to be.

6 Likes

I think you’re confusing love of fp with hatred for oop. The latter isn’t going anywhere in Swift, but a lot of patterns are objectively better solved using fp, and pointing that out, is not the same as hating oop. The opposite is also true.

1 Like

I would be wary about throwing around terms like "objectively" so easily. Whatever studies there are about FP vs OOP productivity are very inconclusive, to my knowledge (which is to be expected, there are way too many confounders).

2 Likes

I'm not throwing it around easily. I use it sparingly. But for some cases fp are better. For others, oop is. Yes, even objectively. Which is better, in general however, is an inconclusive subject. Therefore, Swift is pragmatic and lets you choose.

Anyone suggesting that Swift, or the community, is against one or the other, is ill-informed.

2 Likes

TLDR: Since Foundation multi-platform is an afterthought and since we cannot directly impact the way it evolves, maybe it's not the best choice to have it full with utility features that every platform will need. Having another library that is thought multi-platform first and that we can direct the way to evolve through evolution process might be better.


FWIW Foundation supposed to be https://github.com/apple/swift-corelibs-foundation/blob/master/README.md and its evolution is public https://github.com/apple/swift-corelibs-foundation/blob/master/Docs/Status.md.

But actually, Foundation is more a library for working with Apple OS(iOS, macOS) and SDKs rather than a toolbox for Swift because a lot of it’s type are bound with features Darwin. Backporting Foundation to other OSs is a bad idea because it API rely on a lot of Darwin feature since it bound with AppleOSs, its evolution is not modifiable and multi-platform support is an afterthought.

Having a project driven by the community is really a hard and slow this I understand the resistance to open the evolution of Foundation so we cannot influences(mainly slow) it evolution which might impact Apple SDKs and platforms. In order to make Swift move forward, it needs to have more independent and multi-platform enabled library.

Foundation and Swift don’t seem to share the same goals like multi-platform support and evolution openness. Those are crucial for adaptation and Foundation can’t do them maybe it should be shipped with the Apple SDKs instead of Swift.

I don’t think we should do a Foundation Lite but we should add what is missing to the stdlib or make a new cross-platform library that will handle these task.

We have already seen that SPM, NIO, libSyntax needed some features like Process, URL, Data and re-implementing it.

Maybe we can use some blessed library like express here Large Proposal: Non-Standard Libraries even if would prefer a first party solution.

BTW I know Apple is working hard to bring Foundation on Linux and maybe Windows and a few important features have been recently added but the teams working seems rather small.

Some features of Foundation are developed in the open RFP: OrderedSet.

6 Likes

It's not a slow process people fear - it's a solution that isn't tailored to macOS/iOS but just another sucking cross platform toolkit nobody wants to use.

Foundation does not seem like a Swifty solution and is not easy portable.
I rather think that it should be available only on macOS and that we (Swift) build our own cross-platform API, being it inspired by Foundation or other languages.
It would resolve issues that we have currently such as SwiftNIO not linking with Foundation because of dependencies.

What is so unswifty at Foundation? I don't understand why it is so hated by non apple users.

The fact that some of it doesn't work on non-Darwin systems?
I recently spent a whole day replacing a CSV library that worked locally but wouldn't in our docker container on CI (and production). The reason it didn't work was that some as NSData? casts work on macOS/iOS, but they don't on Linux.

1 Like

This type of bridging has been recently added to Swift Foundation.

I think that people stating that Foundation is not a cross-platform solution are simply saying that because:

  1. Swift Foundation is not fully completed.
  2. Foundation on Darwin is using a different Objective-C based implementation.
  3. Swift Foundation is not at feature parity with Darwin Foundation.
  4. Ignore the history and evolution of Foundation.

Foundation was designed to be a cross-platform basic framework (the Objective-C version has been working on NeXTStep, Darwin, Windows (I think iTunes is currently using it) and Linux (GNUStep)).

I do think that:

  1. Completing Swift Foundation should be a priority of the Swift Project.
  2. The Darwin Foundation Swift SDK overlay and Swift Foundation should be somewhat integrated to allow full cross-platform evolution.
  3. Foundation design should move to a Swift Evolution style. The design direction and roadmap should be published and updated by the Foundation Core Team.
  4. Publishing parts of the Objective-C Foundation source code would allow to make Swift Foundation more similar in implementation and performance to it.
  5. In the long-term it would be great to be able to unify Swift Foundation and Darwin Foundation so all Objective-C entry points would call to the Swift implementation that would be the same in all platforms.
14 Likes

I don’t think you understand the evolution process. Direction is given by the core team, who take our reviews in to consideration. Specifically, proposals are not accepted/rejected based on how many +1s they get.

Also, the thing about OOP doesn’t make sense. It’s one specific implementation strategy, which may or may not be the most appropriate one depending on your needs. Class hierarchies are of course supported in Swift, and I haven’t heard of any plans to change that.

1 Like

I trained as an electronic engineer and wrote lots of C programs for microcontrollers (the kind where you have so little RAM they basically don’t have a heap worth mentioning, and very little stack). It puts you in a mindset where you want to be really conservative about what you import.

My hope is that one day, I might be able to write Swift code for those platforms. There has really been a lot of development in recent years, and some of them even have C++ compilers (the resulting binary size is roughly the same as C - again, you just have to be careful about using the heap). Right now that’s a pipe-dream, of course. I don’t think the Swift standard library is written with such systems in mind, but as least from an API level it should be portable.

Having the language depend so heavily on a relatively large module like Foundation makes me feel like that will never be possible. Foundation has a lot of functionality which just doesn’t make sense on an MCU with no operating system, so I wouldn’t port it to those platforms or use it for those projects.

Generally, that’s fine. 99% of Foundation is stuff an MCU wouldn’t care about (URL_Session_s? Processes? Hah). The notable exceptions are ‘Data’ (which, as I said in a separate thread, I think should be in the standard library), and possibly JSON serialisation.

For the rest, I think Foundation is fine how it is. The APIs could be more swifty and hackable, but that’s a topic for another day.

12 Likes

Yes, I feel that Data should be in the Standard Library (as String and Array already are).

And, of course, Foundation requires more work to feel Swifty. Two simple examples:

  • All the path methods in FileManager should allow URL instead of some allowing String and others URL.
  • FileManager.fileExists(atPath:isDirectory:) should be dead by now and replaced by FileManager.directoryExists(at:)

Simple things like this would probably already been solved if Foundation Evolution was a thing.

3 Likes

It's not a "hate", and not about Foundation, but about Foundation in Swift.
Having to support Foundation and XCTest makes Swift dependent from ObjC, which is a pity.
It is and will limit some evolution on it.
Again issues we have with Foundation/SwiftNIO/URLSession seems like a good example of it.

1 Like

Swift had an aggressive evolution process not only thanks to the community, but mainly because it had few users and the cost to break code was low. This is not true for Foundation that has many decade of history.

You can't simply drop old methods from Foundation like we did with previous Swift languages features and API.

1 Like

Apple keeps marking methods as deprecated in Foundation in each new OS release as well as adding new API.

If Foundation Evolution was a thing we wouldn't remove any API from the binary libraries (or event touch the Objective-C version) but could add new API in the Swift overlay / Swift Foundation and mark methods as deprecated.

Also as Swift becomes a language with ABI stability and source compatibility, Swift Evolution cannot be as aggressive as it once was, but that doesn't make Swift Evolution deprecated.

Swift users are not supposed to have experience with Apple platforms and other languages. Not everyone can easily perform the back-and-forth "mental switches" between C-looking apis, Objective-C-looking apis, and Swift-looking apis, that one sometimes experiences when working with Foundation.

This makes me think about the major work that has been performed by Apple on application frameworks (CoreGraphics, UIKit, others), to make them good Swift citizens:

// if (CGRectContainsPoint(rect, point)) { ... }
if rect.contains(point) { ... }

// CGRect rect2 = UIEdgeInsetsInsetRect(rect1, insets)
let rect2 = rect1.inset(by: insets)

I wonder what the same process, applied to Foundation, could do to help users have a better opinion of Foundation. Of course, this would require a great deal of audit, design, and hard work.

1 Like

I did attempt to play my own game.

Let's look at a Swift 4.2 snippet (adapted from a FAQ sample code):

// Copy a read-only resource into a place where it can be modified.
// Perform this copy only once.
let fileManager = FileManager.default
let path = try fileManager
    .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    .appendingPathComponent("file.extension")
    .path
if !fileManager.fileExists(atPath: path) {
    let resourcePath = Bundle.main.path(forResource: "file", ofType: "extension")!
    try fileManager.copyItem(atPath: resourcePath, toPath: path)
}
doStuff(path: path)

It does a lot:

  1. Look for a standard directory... (with support for Apple platforms, but not only)
  2. ... in user's domain (I lack the skills to know if domains are cross-platform, or purely useful for macOS server and its NeXT ancestors)
  3. URL manipulation
  4. URL to path conversions
  5. Check for file existence (without error handling)
  6. Load a resource's url from a bundle (I don't know if "bundle" is a portable concept, but I guess "resource" is)
  7. copy files (or directories)

What if I would attempt to "modernize" it?

let fileManager = FileManager.default
let url = try fileManager
    .url(for: .applicationSupportDirectory, inDomain: .user, options: .createIfNeeded)
    .appending("file.extension")
if try !fileManager.fileExists(at: url) {
    let resourceURL = Bundle.main.url(forResource: "file.extension")!
    try fileManager.copy(resourceURL, to: url)
}
doStuff(url: url)
  1. I've kept FileManager. This beast does a lot. I'm not there to throw the baby out with the bathwater.
  2. I've kept the notion of standard urls.
  3. I've kept the notion of domains, because I dare not removing them.
  4. Replaced the create: true boolean argument with options: .createIfNeeded
  5. Simplified appendingPathComponent to appending. But I guess this change is ill-advised, because you are not supposed to append strings with forward slashes, here.
  6. Never converted urls to paths
  7. Made FileManager.fileExists throwing
  8. Made resource url extraction legible
  9. Compacted FileManager.copy(_:at:)

I'm not that sure that it is much better looking, as a matter of fact. :wink:

1 Like

No disagreements that FileManager is a beast that does a lot (more than I even understand), but I do think it should be replaced :grimacing:. Even your updated version doesn't feel very swifty to me. It lacks a lot of the strongly typed features that so much of the language has in other areas.

Paths shouldn't just be generic strings/urls (I'm ok with the underlying storage being a string/url). There should be separate types for the various path types (ie: DirectoryPath, FilePath, etc) because each path type has different sets of functionality. Rather than interacting with paths via FileManager, I'd prefer to be able to do everything on a Path directly. For example, you shouldn't be able to incidentally try getting the subpaths of a FilePath, this is something that should be limited to DirectoryPaths.

Converting your example to what my ideal Swift Path library would be:

let supportDir = DirectoryPath.applicationSupportDirectory(inDomain: .user, options: .createIfNeeded)
let newFile: FilePath = supportDir + "file.extension"
if !newFile.exists {
    let resource: FilePath = Bundle.main.file(forResource: "file.extension")
    try resource.copy(to: newFile)
}
doStuff(path: newFile)

Of course, I'm not very familiar with Bundle nor do I have any idea what the FileManager domains stuff is.

2 Likes