I’ve been on a Swift + R bender for a while now, but have been envious of the pure macOS/iOS (et al) folks who get to use Apple’s seriously ++good machine learning libraries, which are even more robust on the new M1 hardware (it’s cool having hardware components dedicated to improving the performance of built models).
Sure, it’s pretty straightforward to make a command-line utility that can take data input, run them through models, then haul the data back into R, but I figured it was about time that Swift got the “Rust” and “Go” treatment in terms of letting R call compiled Swift code directly. Thankfully, none of this involves using Xcode since it’s one of the world’s worst IDEs.
To play along at home you’ll need macOS and at least the command line tools installed (I don’t think this requires a full Xcode install, but y’all can let me know if it does in the comments). If you can enter swiftc
at a terminal prompt and get back <unknown>:0: error: no input files
then you’re good-to-go.
Hello, Swift!
To keep this post short (since I’ll be adding this entire concept to the SwiftR tome), we’ll be super-focused and just build a shared library we can dynamically load into R. That library will have one function which will be to let us say hello to the planet with a customized greeting.
Make a new directory for this effort (I called mine greetings
) and create a greetings.swift
file with the following contents:
All this code is also in this gist.
@_cdecl ("greetings_from")
public func greetings_from(_ who: SEXP) -> SEXP {
print("Greetings, 🌎, it's \(String(cString: R_CHAR(STRING_ELT(who, 0))))!")
return(R_NilValue)
}
Before I explain what’s going on there, also create a geetings.h
file with the following contents:
#define USE_RINTERNALS
#include <R.h>
#include <Rinternals.h>
const char* R_CHAR(SEXP x);
In the Swift file, there’s a single function that takes an R SEXP
and converts it into a Swift String
which is then routed to stdout
(not a “great” R idiom, but benign enough for an intro example). Swift functions aren’t C functions and on their own do not adhere to C calling conventions. Unfortunately R’s ability to work with dynamic library code requires such a contract to be in place. Thankfully, the Swift Language Overlords provided us with the ability to instruct the compiler to create library code that will force the calling conventions to be C-like (that’s what the @cdecl
is for).
We’re using SEXP
, some R C-interface functions, and even the C version of NULL
in the Swift code, but we haven’t done anything in the Swift file to tell Swift about the existence of these elements. That’s what the C header file is for (I added the R_CHAR
declaration since complex C macros don’t work in Swift).
Now, all we need to do is make sure the compiler knows about the header file (which is a “bridge” between C and Swift), where the R framework is, and that we want to generate a library vs a binary executable file as we compile the code. Make sure you’re in the same directory as both the .swift
and .h
file and execute the following at a terminal prompt:
swiftc \
-I /Library/Frameworks/R.framework/Headers \ # where the R headers are
-F/Library/Frameworks \ # where the R.framework lives
-framework R \ # we want to link against the R framework
-import-objc-header greetings.h \ # this is our bridging header which will make R C things available to Swift
-emit-library \ # we want a library, not an exe
greetings.swift # our file!
If all goes well, you should have a libgreetings.dylib
shared library in that directory.
Now, fire up a R console session in that directory and do:
greetings_lib <- dyn.load("libgreetings.dylib")
If there are no errors, the shared library has been loaded into your R session and we can use the function we just made! Let’s wrap it in an R function so we’re not constantly typing .Call(…)
:
greetings_from <- function(who = "me") {
invisible(.Call("greetings_from", as.character(who[1])))
}
I also took the opportunity to make sure we are sending a length-1 character vector to the C/Swift function.
Now, say hello!
greetings_from("hrbrmstr")
And you should see:
Greetings, 🌎, it's hrbrmstr!
FIN
We’ll stop there for now, but hopefully this small introduction has shown how straightforward it can be to bridge Swift & R in the other direction.
I’ll have another post that shows how to extend this toy example to use one of Apple’s natural language processing libraries, and may even do one more on how to put all this into a package before I shunt all the individual posts into a few book chapters.
Nos Autem Non In Antebellum; Bella Iam Inceperat
(Leading this with the periodic warning/reminder that this blog occasionally breaks from technical content and has category-based RSS feeds which can be used to ensure one never see non-technical content.)
Every decent human (which excludes 74,222,958 🇺🇸 who voted for this, now 100% undeniable, traitor) with knowledge of this past week’s tragic events is likely still processing — and will be for a while — what happened; I am no exception. The Feedly board I set up to save content I’ve been pouring over has 113 articles in it, so far.
Different aspects of the costume-clad, treasonous chaos have hit me daily, if not hourly.
Two newspaper paragraphs, each one about a different victim, have bubbled up to surface thoughts more often than much of the other stories of the week.
One is about Erin Schaff, a brave, talented journalist from the New York Times:
No one came.
People just watched.
While I am deeply shocked, outraged, and saddened by what happened to Erin, I am not surprised, given that the President of the United States wants journalists to be executed for regularly giving his 2017-2020 reality show bad reviews by stating undeniable facts. Furthermore, he has continually cultivated disdain and hatred for the media in his regiment of cult followers.
Erin is lucky to be alive, even if that means living in the ruins of a failed, so-called democracy.
President Trump is responsible for Erin’s assault, and he is going to get away with it.
The other is about Ashli Babbitt, the troubled insurrectionist who died assaulting the Capitol:
After the February 2020 impeachment proceedings failed to do anything substantive, the President boasted of feeling “untouchable”; and, in at a campaign rally in 2016, then Republican presidential candidate Trump boasted that he could “shoot somebody and not lose any voters.”
Trump has taken the lives of hundreds of thousands of Americans. And, while the gun wasn’t in his stubby hand, he is fully responsible for this woman’s shooting and death.
So, Trump was right: he is going to get away with it.
Mike Pence, Ted Cruz, Josh Hawley, Lindsey Graham, Susan Collins, Mitch McConnell, and a few hundred other evil, self-serving, elected cowards are all unindicted co-conspirators to Erin’s assault and Ashley’s death, as are countless “news” and talk show hosts.
Beyond what happened to these two women, this traitorous cabal also helped orchestrate this week’s current crescendo to Trump’s term in office.
I say “current” because there’s a non-zero chance of increased violence and bloodshed before January 20th despite Trump being deplatformed.
I have lost all hope that Trump will face any tangible consequences for his actions, which will only serve to embolden other wanna-be dictators like Cruz and Hawley.
What’s worse is that even after Biden’s victory was finally 100% sealed and one of America’s most cherished institutions was ransacked, the Trump supporters near me (rural-ish Maine) still, proudly, have their 2020 Trump campaign signs up and were very likely laughing and cheering the insurrection while Erin was being assaulted and Ashley’s life was ebbing away.
Even the Court Evangelicals have doubled-down in their support of Trump.
I (literally) pray I’m wrong, but it seems inevitable that the violence and bloodshed will continue through and after the 20th. As Biden tries to (also, literally) heal America by bringing science-fueled, centralized, enforced standards to quell the carnage of Covid, we will very likely and regularly see regional repeats of this week’s contemptible acts. As he and his administration attempt to right the many, many wrongs of the past four years (and more), these necessary actions will further push the ilk of this week to regularly manifest their entitlement-fueled rage.
Nos autem non in antebellum; bella iam inceperat.