
Lightweight API Design in Swift - ingve
https://www.swiftbysundell.com/articles/lightweight-api-design-in-swift/
======
nickmqb
> Since each transform needs to perform vastly different image operations,
> using an enum in this case would’ve forced us to write one massive switch
> statement to handle each and every one of those operations — which would
> most likely become somewhat of a nightmare to maintain.

I don't mean to pick on the author, but I've seen this line of reasoning a few
times before. It's the same argument that has been used in the past to justify
inheritance hierarchies in OOP languages. I used to believe it too. However, I
don't think this is actually true. In fact, I'd argue the opposite: switch
statements, if used well, are _extremely maintainable_. Even though a switch
statement might handle many cases, it does not become more complex [1] by
doing so. If we're concerned about the length of the individual cases, we can
easily replace each one with a function call. Fundamentally, in the example
from the article, we'd like to map a piece of data (the operation with its
parameters) to some behavior (actually performing the operation). A switch
statement is one of the simplest ways to do that.

[1] [https://www.infoq.com/presentations/Simple-Made-
Easy/](https://www.infoq.com/presentations/Simple-Made-Easy/)

~~~
flipgimble
What you describe is called the Expression Problem [1] in programming language
design and there is no simple formulaic answer on which method is better. I
think you have to consider many aspects of your code's current design and
possible future evolution when deciding which approach to use. For example: do
you expect to have more types of transforms, or more operations/method per
type of transform? It also means you can't nitpick a limited tutorial for
focusing on one approach vs. the other.

Fortunately swift (as well as Rust or Kotlin) has excellent modern toolbox
that includes protocol conformance and algebraic data types so you can use
either one.

Keep in mind that swift protocols avoid many of the pitfalls of Java\C++
school of OOP design you might have seen before that can only express "is-a"
relationships.

[1]
[https://en.wikipedia.org/wiki/Expression_problem](https://en.wikipedia.org/wiki/Expression_problem)

~~~
pjmlp
Java and C++ have no issues representing has-a relationships.

The issue is developers not learning how to use their tools.

------
skohan
This post does a good job of highlighting one of my favorite things about
Swift: it's almost always possible to express things _exactly_ how you want.
And Swift is getting even better at this with features like property wrappers.

Swift has a few hurdles to overcome, like better cross-platform support,
solutions to the performance cliffs related to ARC, and more parsimonious
copy-handling of value types, but I think it has the potential to be one of
the most productive and powerful languages for building software in the next
few decades.

~~~
Razengan
I love Swift as well and prefer to use it for everything wherever I can, but
you may find yourself running into walls if you try to get too smart with it.
Though I suppose that’s inevitable in any language.

An example of something I’ve been struggling against:

[https://forums.swift.org/t/swiftui-extension-for-os-
specific...](https://forums.swift.org/t/swiftui-extension-for-os-specific-
view-modifiers-that-seems-too-arcane-to-implement/30897)

------
saagarjha
Making DSLs in Swift is surprisingly easy, as this shows, but a brief word of
warning: if you overdo it, your API, while pretty at the use-site, can end up
being hard to actually use–you'll create what's essentially a read-only
language. SwiftUI has taken this style of design to about as far as it's
possible to go, and while it's pleasant to read it has a number of pains with
regards to API discoverability. Plus the compiler absolutely hates everything
about it :(

~~~
skohan
I have the feeling that SwiftUI was slightly rushed. Swift's language
development has been slow and methodical, with a lot of care put into making
sure new language features are not only substantially valuable and well
conceived from a design standpoint, but also that they don't risk moving the
language in a direction which would limit potentially better possibilities in
the future. This can be frustrating, as it means it takes a long time for
obvious and useful features to make their way into the language (looking at
you variadic generics), but the tradeoff is that Swift is a very elegant and
well-designed language which doesn't have some of the awkward edges you see in
other languages which have to exist for legacy reasons, like you see in C++.

In that respect, it seems like function builders skipped that whole process.
It's not that they're a bad feature, and similar facilities exist in other
languages, but the fact that they were added to the language without a lengthy
vetting process seems very out of character.

In a way I don't even know if it's really needed. I have a project which uses
declarative code to achieve similar goals to SwiftUI, and you can get really
close without function builders. For instance, you can write code like this:

    
    
        var view: [View] {[
            Foo(...),
            Bar(...),
            Baz(...),
        ]}
    

It's not quite as clean as the function builder approach, and it doesn't have
all the same benefits (i.e. producing a typed result vs an array of protocol
instances) but it's very easy to understand and doesn't require any additional
language features.

My theory is that since Google has been pushing Flutter aggressively, Apple
needed to answer with a "modern" FRP-style workflow for iOS to retain its
developer mindshare.

~~~
gwking
It may also have been rushed due to the ABI stability deadline. I'm
speculating, but it is easy to imagine that the SwiftUI team saw the window
for getting new language-level features closing.

More generally, Apple's yearly splash model seems to dictate that teams either
release or wait 12 months, if waiting is even an option at all. It makes me
wonder how many Apple APIs would have been revised under a more flexible
release model.

~~~
skohan
> is easy to imagine that the SwiftUI team saw the window for getting new
> language-level features closing.

Would function builders have much impact on ABI stability? My assumption was
that they are essentially just a front-end transform: i.e. the SIL generated
from a function builder would be equivalent to writing it out "long hand" \-
the same way that Codables can essentially be thought of as the compiler
writing out boilerplate Swift code for you.

------
gigatexal
Off topic, but I've been following Sundell for some time now from his podcast
and think he's doing some really cool things in the Swift space.

~~~
Austin_Conlon
Discovered him from his podcast as well, it's wonderful. Here's an example
interview with Chris Lattner:
[https://www.swiftbysundell.com/podcast/50/](https://www.swiftbysundell.com/podcast/50/).

~~~
gigatexal
Thanks for this! I didn't know they did one with Lattner, should be awesome.

------
sgt
Probably one of the most elegant languages around.

~~~
tigershark
F# and Kotlin seem better to me, and don’t have that hideous syntax for the
closures. But it’s for sure another world compared to the awful objective c.

~~~
mojuba
I wouldn't say Kotlin is significantly better than Swift in terms of syntax,
they are largely similar to the point you sometimes think: someone copied this
from the other, I wonder who was the first.

To me personally though the showstopper for Kotlin is that it runs on Java VM.
That was the design goal of course, i.e. to become a major "upgrade" of Java
esp. on Android. Java VM (with its GC) is a creation of the pre-mobile era
when all we had was desktop computers, ever growing processing power and an
assumption that you have infinite supply of electric power. It's a different
philosophy and design. I think Google's choice of using it on a mobile
platform was unfortunate, though at the time (i.e. before the iPhone) many
popular mobile platforms did run on Java VM so it seemed like a natural
choice.

~~~
pjmlp
Swift's ARC implementation has a little bit of performance catchup to do in
regards to tracing GCs.

[https://github.com/emmericp/ixy](https://github.com/emmericp/ixy)

~~~
skohan
IMO this example should be taken with a grain of salt, since at a cursory
glance it looks like they are leaning heavily on reference-types, which is not
really idiomatic swift, and is a worst-case for tightly-looping code which
this is. I would love to spend even a half hour trying to optimize this code
to see what the results could be, but there is specialized hardware required
to run the benchmark, so it's not really practical for 3rd parties to attempt
to achieve a better result.

It's fair to say that ARC has performance issues, but the story is a bit more
complex than that. By relying heavily on value types, it's possible to write
Swift code which carries very little ARC cost (although there are additional
issues with copy-on-write performance). Also ARC is more memory efficient than
most GC solutions, so there are tradeoffs involved.

~~~
pjmlp
Languages with tracing GC implementations also have value types, thus they
would keep winning.

Top performance reference counting implementations are tracing GC in disguise.

------
jlubawy
Reminds me of this API design post for Go:
[https://dave.cheney.net/2014/10/17/functional-options-for-
fr...](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-
apis)

