I'm attempting to parse Swift Predicates generated by the #Predicate macro and turn them into a WHERE clause for querying a Couchbase database via SQL++.
Regex exposes no public API (that I can find) for retrieving the pattern that was used to initialize the Regex. In other words:
let regex = /[a-z]/
let pattern: String = regex.pattern // "[a-z]" does not exist.
Why does Regex not expose the original pattern? Even a Regex built with the RegexBuilder must ultimately be translatable into a standard, string Regular Expression—that's the only thing that's able to leave the boundary of Swift and interact with everything else (SQLite, PHP, etc.)
How does the SwiftData team translate the Regex into an SQLite query string?
In general, Regexes do not have a “literal pattern” (the builder syntax allows you to construct a Regex that cannot be represented as a literal or string).
Predicate is OK working within the limitations imposed by that, which is why the SPI exists, but it’s not obvious that it makes for a great API. If there are other uses that make good sense within the semantic limitations, I’d be open to making it available as API, but so far not many have come forward.
First, 90% of Regexes are going to be “mainstream” expressions that are representable as a literal pattern. Match an email. Match a phone number. Etc. The proportion of Regexes that leverage Swift-only features is probably small, no?
Second, what’s the downside? The SPI already returns an optional. Why prevent developers who DO need the capability from having it just because it’s not “common enough”? There are public capabilities (parsing Predicate expressions) that are blocked because this is private. Developers can handle the cases where a Regex has no literal pattern equivalent.
I remember at one point making a custom type that effectively just wrapped a Regex and could only be initialized by a String, but because it needed to be LosslessStringConvertible, I had to store both the Regex and the String so that I could use the latter for description and nothing else. I asked about it on StackOverflow at the time (over two years ago now): How to convert a Swift 5.7 Regex back to a string? - Stack Overflow -- and it seems like @bdkjones just left an answer about the private API.
These are both classic examples of things that are a pain in the butt to do correctly with a regex (and most regexes you see for them will be wrong in some way), but a custom Regex wrapping a data detector can do very cleanly.
seems like @bdkjones just left an answer about the private API.
Ha, yep. Your question was the start of this latest rabbit hole.
These are both classic examples of things that are a pain in the butt to do correctly with a regex (and most regexes you see for them will be wrong in some way), but a custom Regex wrapping a data detector can do very cleanly.
Everything is a pain in the butt to do with Regex. But nothing about that justifies keeping _literalPattern private. Developers inside of Apple clearly had a need to represent Regex as a string. Developers outside of Apple have the same need.
If there’s demand for the ability and the private API isn’t unstable or full of footguns or still undergoing major, breaking revisions, it should be available. The “eh, we just haven’t had enough use cases yet” doesn’t make a lot of sense—the cases that DO exist are valid and helpful.
It is full of footguns—namely that it can fail (and frequently will), contrary to many developers expectations, as demonstrated by this thread.
That’s not insurmountable, but “what will your fallback be when the Regex doesn’t have a string representation” is a real issue (and if you want to disallow those cases, there’s already pretty good API for that—you use the string representation that you already have). It’s a perfectly reasonable pitch, but someone has to write it.
and if you want to disallow those cases, there’s already pretty good API for that—you use the string representation that you already have)
?
I don’t have a string representation. The Predicate expression hands me a Regex needle and a KeyPath to the haystack. If I had a string representation of the needle, I wouldn’t need this.
As for what to do if _literalPattern returns nil: in my case, I throw an error saying that the #Predicate’s regex can’t be transformed into an SQL++ query. Thats not a big issue and having the ability to handle 80% of Regexes is better than having the ability to handle 0% of them.
I don’t really have a dog in this fight anymore. The private API will work perfectly for me.
I’m just pushing back against the idea that there isn’t “enough” demand for the feature to justify exposing it publicly. There is demand. And while it may not be super common, the people who need this ability have zero other workarounds (if they’re dealing with App Stores).
Sure, it can fail in edge cases. But the documentation comment makes that very clear. Publish the API with that warning and let developers cover the 80% of cases that work just fine.
Putting the public API on Predicate (or whatever other type/macro wraps a regex) would probably be cleaner in this case, because by the time it’s a predicate, you already have the necessary guarantee. But sure, exposing the building block would also be useful in other cases. It only needs someone to write a proposal.
Ah, I see what you mean now. (Should not pull up this stuff on iPhone screens; I saw Regex in the signature but did not pay close enough attention to the type.). The Regex in the predicate expression is PredicateRegex, which already has a stringRepresentation populated. That's fair for this case, but I still see no reason to keep _literalPattern private just because it won't work for every conceivable Regex.
It's a great tool to have when you need it, which will virtually always be at the boundary between Swift and some other language.