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 by Gordon Fontenot and this post on binding views to object values by Srđan Rašić (that I like is similar to the approach that I was trying to take with my binding library, described poorly in this post). 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, 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.

 
1
Kudos
 
1
Kudos

Now read this

Partial vs. Complete Functions

Whoo boy, stick with this seemingly complicated article (linked in the title), because it directly addresses that subtle issue that I face regularly when functions or methods that I write can’t properly map directly all input to your... Continue →