i want to push back a little on the basic premise of this idea, which is that stronger typing is always better.
i think that if you are someone who has chosen swift as your favorite programming language, as opposed to something like python, this isn’t even an opinion, it is accepted as a basic truth. and because of that i think it’s really valuable to be aware of overtyping as something that afflicts swift developers.
i’m going to give an example inspired by my own experience as someone susceptible to overtyping. let’s suppose we’re writing a library that interacts with websites in the swift ecosystem. for type safety, you might assume some useful URL types might include:
/// A URL pointing to a forums.swift.org page.
public
struct SwiftForumsURL:RawRepresentable<PercentEncodedString>
/// A URL pointing to a swift.org page.
public
struct SwiftOrgURL:RawRepresentable<PercentEncodedString>
/// A URL pointing to a en.wikipedia.org page.
public
struct WikipediaURL:RawRepresentable<PercentEncodedString>
is this a good idea? of course it’s a good idea! how else would you express
func loadSwiftForumsPost(_ url:SwiftForumsURL) -> SwiftForumsPost?
?
you certainly wouldn’t want to Accidentally Pass a wikipedia URL to loadSwiftForumsPost(_:)
. it’s hard to see any downsides to such a statically-sound arsenal of types.
and yet there are downsides, they come from the basic fact that code must eventually interface with other code. let’s suppose you’re now writing the logic that renders forum posts.
import HTML
import SwiftURLTypes
struct DiscourseForumsPost
{
let url:SwiftForumsURL
let content:HTML
}
but do you really want the HTML rendering module to have a dependency on the SwiftURLTypes
module? why should DiscourseForumsPost
care what kind of URL it has? this is an unnecessary coupling between two components of an app that really ought to function independently. if you had not written the SwiftURLTypes
beforehand, you would have simply represented the url
property with a String
. instead, SwiftURLTypes
has become a universal dependency that every other target in your project must import.
at this point, you might spend some time refactoring the project into a more sophisticated dependency graph.
// URL module
.target(name: "SwiftURLTypes", dependencies: ["PercentEncoding"]),
// Discourse module
.target(name: "DiscourseForums", dependencies: ["HTML"]),
// Overlay module for API sugar
.target(name: "_DiscourseForums_SwiftURLTypes",
dependencies: ["DiscourseForums", "SwiftURLTypes"]),
you might even convince yourself you’ve gotten the best of both worlds with the overlay module. but this took a lot of time and you added a lot of public API cruft to get SwiftURLTypes
to interoperate smoothly with all the other components of your project.
was it worth it?