Calling Haskell from Swift
alt-romes.github.io
alt-romes.github.io
[0] https://www.destroyallsoftware.com/screencasts/catalog/funct...
The really nice thing about the original interop code, though, is that GHC’s new WASM backend uses essentially the same foreign function interface to export functions to JavaScript. So with only some minor modifications, I was able to get the same program working on a webpage [1], which I think is pretty cool.
[0] https://github.com/bradrn/brassica, and see in particular the old version of https://github.com/bradrn/brassica/blob/93d4353c5736018ec094...
[1] At the risk of DDOS’ing my poor little home server: https://bradrn.com/brassica/
* https://defn.io/2023/03/19/racketfest-talk-2023/
* https://defn.io/2023/02/04/racket-foreign-callouts-to-swift/
I also started out using JSON to exchange data between the languages, but eventually wiring everything manually became an annoyance so I came up with a little Racket DSL[1] that automatically serializes and deserializes between the two languages (at runtime in Racket & using code generation in Swift). So far, I've built two apps with this approach, and I think it's great:
* https://github.com/Bogdanp/Franz (macOS, Windows, Linux)
* https://github.com/Bogdanp/remember (macOS, iOS)
var size = buf_size
c_birthday(rawPtr.baseAddress, data_len, res_ptr.baseAddress, &size)
In this case, since you know your size is fixed, and it's actually quite large (1 MB) it is never going to fit on the stack anyways. So there is no need to use a function that will try to do that: just use Array's init(unsafeUninitializedCapacity:initializingWith:) initializer instead, and you will bridge it into Swift's lifetime management without having to do the whole closure dance. Finally, since your API is unlikely to modify the buffer you pass it in, try using withUnsafeBytes instead of withUnsafeMutableBytes (and correctly annotating the pointer with const in your bridge).Haskell supports a C API/ABI directly, as does Swift. E.g., Swift String can be shared as cstring, i.e., a null-terminated byte array, with no allocations needed. Since both languages support the (per-platform) C API/ABI, that would seem to be the best option, with home-grown serialization a risk (or perhaps a fallback where necessary).
In most cases, Swift simply handles the C-type without needing any special bridging. In some cases of ambiguity, you can add attributes on the C declarations to tell the Swift compiler how to handle the data and call.
Haskell also supports a range of C-ABI annotations, but that's on the Haskell language side, likely invisible to Swift.
A super-helpful contribution would be to see what range of types bridge without annotations and what additional types can be handled with annotations.
But since the author did go to all the effort of marshaling, they must have discovered unavoidable limitations. It would be most helpful to know what those were!
Edit: here's a tutorial for swift calling Haskell from 5 years ago, before the huge changes in Swift 5.9 for C/C++ interoperability:
> The code in our GitHub repository 20 has indeed shipped as part of iOS 17 and macOS Sonoma. That includes our new, dramatically faster implementations of JSONDecoder and Calendar.
https://forums.swift.org/t/any-updates-on-the-new-foundation...
That comment was from Nov 2023, and if you look at the GitHub repo, you’ll see that the JSON part of Foundation has commits within even the last month (perhaps getting ready for Swift 6.0?).
Problem with iOS is this constant need for objc compatibility and transparent bridging, which really messes things up a lot..
The post is interesting otherwise.
I’ve used a lot of IDEs in my day, and it was hands down the worst.