[swift-evolution] [Review] SE-0144: Allow Single Dollar Sign as a Valid Identifier

Benjamin Spratling bspratling at mac.com
Thu Oct 20 04:52:54 CDT 2016


Howdy,
	It seems the main question in discussing this proposal is “If $0, $1, $2, $3 are automatic closure parameters, then what is “$”?”.  Another thing that has also recently come to my attention that Swift does not have a “result of previous expression” magic identifier as some functional languages do.  Combine this with my difficulty in creating closures which automatically bind weakly to the variable, and I’m looking at a potential meaning for “$” consistent with all of these goals.

First off, I don’t like the “$0, $1, $2, …” feature, but I can respect it.  I mainly don’t like it because in English “$” is defined as “dollar”, not “magic variable number _”.  However, it appears that in Swift, “$” really does mean “magic variable number _”, and some developers find it very useful.

I’m not looking to routinely violate the law of Demeter, but occasionally in data conversion, it’s really unavoidable.  Sometimes, I need to convert to a type with a non-optional argument, but my argument is Optional.  Here, for instance:

struct XMLNode {
	var attributes:[String:String]
}

struct Note {
	/// half-steps from middle-C, nil means the note is a rest
	var pitch:Int?
	//more properties
	init?(xmlNode: XMLNode) {
		if let pitchString:String = xmlNode.attributes[“pitch”], let pitchInt:Int = Int(pitchString) {
			pitch = pitchInt
		}
		//more code
	}
}

I could go write an extension on String to provide a computed integer property, but I’m not just talking about one case, I’m talking about all cases where we convert to types in this way.

If “$” meant “identifier of result of previous expression”, I could write:

init?(xmlNode: XMLNode) {
	pitch = xmlNode.attributes[“pitch”]?Int($)
	//more code
}

instead.  Much cleaner.

Now, in my mind the first question is: “don’t I need another operator after the “?” ? “  And that got me thinking about unowned/weak capture in closures and UndoManager blocks.  If the reference is captured separately from the actions on the reference, then strong/weak/unowned really isn’t an argument for those cases.  It’s like I want an autoclosure that takes 1 instead of 0 arguments, the 1 generic value from the previous expression, which, incidentally would be represented by “$”—if it were explicitly referenced.  The optional chaining operator right now, as I understand it, is compiler magic.  But what if there were a language construct for such features as there is for optimized evaluation with ??, &&, and || ?  It would allow me to create my own “?”-like behaviors.  

Which operator it is, I don’t much care; I am looking closely at “\”.  Finally, there is a difference between a 1-argument auto closure that acts as if it continued an expression, i.e. _?._ and one which places the previous result as an argument into a self-contained expression.  But if the leading-dot, i.e. “.intValue”, syntax is itself just syntactic sugar for writing “$.intValue”, then there really isn’t a difference.

So the chaining optional operator becomes defined in the language as:

nocapture operator ? : WhateverPrecedenceGroup

infix func ?<LReturnType, RReturnType>(lhs:@autoclosure()->(LReturnType?), rhs:@autoclosure (LReturnType)->(RReturnType))-> RReturnType? {
	guard let lhs = lhs() else { return nil }
	return rhs(lhs)
}

and I can extract a closure which does not capture “self” at all:

class Controller {
	func provideClosure()->(Controller)->Result {
		return self\.doSomething	//doesn’t capture self
	}
	func doSomething()->Result {
		//write me
	}
}

So “\” becomes:

nocapture operator \ : WhateverPrecedenceGroup

and the compiler inserts the magic of creating the “nocapture” operators, which parses nearly identically to how it works today.

Calls to UndoManager become cleaner:

class Controller {
	var color:Color {
		didSet {
			undoManager.register(withTarget:self, handler:{ (controller:Controller)->Void in
				controller.color = oldValue
			})
		}
	}
}

becomes:

class Controller {
	var color:Color {
		didSet {
			undoManager.register(withTarget:self, handler:self\.color = oldColor)
		}
	}
}


So, doing all this would:

- Give $, $0, $1, $2 consistent meanings, “the argument”.
- Solve the problem of quickly obtaining a weak / unowned closure references by creating generic 1-parameter autoclosures.
- Allow inits in optional chains.

Alternatively:

Perhaps use $0 instead inside my 1-argument auto closures, but to me it implies there could be more, and could interfere with these from an enclosing closure.

Perhaps “?” is the operator to use for creating the 1-argument closures, but to me it implies optional-related behavior

Perhaps this really is just too many “dots”, and we want to move away from the functional style, but developers are loving it.

Perhaps the UndoManager’s generic syntax is sufficient.

Perhaps I’ve missed something critical in the grammar.

Thanks your reading and consideration.  Swift on!



More information about the swift-evolution mailing list