Swift Operators – Custom Operator Wishes
As I am reading more about swift and what people are doing with it, I do love seeing some of the ways that it can bring conciseness, particularly with custom operators. Two recent examples that inspired this post are the [Runes functional operators][runes] by [Gordon Fontenot][gordonTwitter] and this [post on binding views to object values][bindPost] by [Srđan Rašić][srTwitter] (that I like is similar to the approach that I was trying to take with my binding library, described poorly in [this post][bindObject]). But I am also really concerned about all the expected operator abuse, operator overload abuse, etc. I am also really concerned about the opacity that results from excessive concision. Having used CoffeeScript for a while at work, I can see the strengths and weaknesses of concision.
The runes example is a great example of the operators being a great way to replace the burden of parentheses and syntactical restrictions that repeated applications of something like
let x = xs.map { add($0, 2) }
compared to
let x = x<*>add(2)
BUT, OOPS! I used <*>
, which means apply()
, instead of <^>
which means map()
! How would I know that <*>
means map without first learning this random library syntax.
NOTE: For the record, in no way do I consider these efforts to be operator abuse. This is a great idea, and by promoting it and open-sourcing it, it may help create something of a standard, but until it is literally canonical, there is no way for this to be immediately readable code.
Here is the binding operator, cited above, which is also a well designed operator:
name ->> nameLabel
It seems like a really great way to indicate the same thing written here
nameLabel.textBond.bind(name)
But, again, how the heck would I know the meaning of that operator, without previously knowing the library?
It occurred to me that these would both be lovely examples of the utility of an operator syntax that would alphanumeric characters in some language-level-defined way. Perhaps even with an upper limit of allowed characters.
so for runes we could transform
array<*>thing
to something like
array<apply>thing
and for the bind example we could change
name->>nameLabel
to
name<binds>nameLabel
There are probably better ways to define how alpha-numeric operators could work, but the point is that by allowing such characters, we don’t have to search for their meanings as much, nor try to infer their meanings as much.
As a final example, taken from the github repo for [Argo][argoGithub], here is a lovely function that, despite very well chosen operators, was unreadable without previously studying the custom operators:
<^> j <| "id"
<*> j <| "name"
<*> j <|? "email" // Use ? for parsing optional values
<*> j <| "role" // Custom types that also conform to JSONDecodable just work
<*> j <| ["company", "name"] // Parse nested objects
<*> j <|| "friends" // parse arrays of objects
}
But when re-written, it is approaching readability without losing much concision:
static func decode(j: JSONValue) -> User? {
return User.create
<map> j <valFor| "id"
<apply> j <valFor| "name"
<apply> j <valFor|? "email" // Use ? for parsing optional values
<apply> j <valFor| "role" // Custom types that also conform to JSONDecodable just work
<apply> j <valFor| ["company", "name"] // Parse nested objects
<apply> j <valsFor|| "friends" // parse arrays of objects
}
Last thought. I wrote this post a few days ago and had to let it sit before posting it. Upon reviewing the code snippets, I am even more struck by how helpful it would be. Other than the bind
operator, I could not remember which operator was which. I had no context to distinguish map
, and which was apply
.
[srTwitter]: https://twitter.com/srdanrasic
[gordonTwitter]: https://twitter.com/GFontenot
[runes]: https://github.com/thoughtbot/Runes
[bindPost]: http://five.agency/solving-the-binding-problem-with-swift/
[argoGithub]: https://github.com/thoughtbot/Argo
[bindObject]: https://github.com/chrisbrandow/maintainObject