"reuse of LHS arguments on RHS of assignment"

I'm not sure if this type of feature has a name but consider the following where I specify a dictionary key on the left hand side of an assignment and want to use it again in the RHS:

protocol Controlable {
    var controlCommands: [String] { get }
}

struct Device: Controlable {
    let name: String
    var controlCommands: [String]
}

var controlables = [String: Controlable]()

controlables["Lounge Light"] = Device(name: "Lounge Light", controlCommands: ["On","Off"])

That last line would be a lot nicer if it could be written as, say

controlables["Lounge Light"] = Device(name: $0, controlCommands: ["On","Off"])

Where $0 represents the "Lounge Light" String key being added to the dictionary, or more generically, an argument used on the left side of an assignment.

Now you might say that in a real world example, "Lounge Light" would probably have a reference and that reference could simply be used instead, and that's true, but there could be other scenarios where the value is being generated, and the value is useful for the assignment:

controlables[generateUniqueRef()] = Device(name: $0, controlCommands: ["On","Off"])

Does Swift already have something like this? Does something like this have a name, for example using $0 in closures is called anonymous closure arguments, so I guess I'm looking for something like "reuse of LHS arguments on RHS of assignment"

I don't think this pattern has a specific name, but you can use a closure for that

{
    controlables[$0] = Device(name: $0, controlCommands: ["On","Off"])
}(generateUniqueRef())
1 Like

Or via extension of Dictionary:

extension Dictionary {
  mutating func set( _ key: Key, value: (Key) -> (Value)) {
    self[key] = value(key)
  }
}

controlables.set( "Lounge Light") { Device(name: $0, controlCommands: ["On", "Off"]) }
2 Likes

Wow what a great forum, two excellent responses with cool solutions in less than an hour. Thanks @cukr & @lassejansen

I love how expressive Swift is and how people find interesting ways to craft solutions.

I am still wondering about the original syntax I used though. It seems like a continuation of the idea of: counter += 1 that perhaps hasn't been pursued? The ability to relate LHS and RHS without needing to wrap it in a structure.

So you mean that you could write your counter example like this?

counter = $0 + 1

Not a bad idea I guess, but you may shadow closure parameters if you are in a closure. Also not sure if this feature would be used often enough to warrant a language extension.

Kind of, I mean languages already recognise:

counter = counter + 1

as a common pattern where the same reference is used on both LHS and RHS, and so allows the shortcut

counter += 1

Which is fine as is, but this convenience is only for simple expressions. Just wondering about if it could be extended more generically, and if any languages do so.

The standard and idiomatic way to re-use a value in multiple places, in Swift just as in everything else, is to make a local variable for it. @cukr's closure trick is a standard desugaring of that, but it's not actually better than making a local variable.

4 Likes

You could make your own operator to do something like this with a closure

^^ is probably not what you'd use, just used it as an example

infix operator ^^ : AdditionPrecedence

public func ^^<In>(lhs: In, rhs: (In) -> ()) {
    return rhs(lhs)
}
protocol Controlable {
    var controlCommands: [String] { get }
}

struct Device: Controlable {
    let name: String
    var controlCommands: [String]
}

var controlables = [String: Controlable]()

"Lounge Light" ^^ { controlables[$0] = Device(name: $0, controlCommands: ["On","Off"]) }

Probably overkill and not how I would do it, but it's kind of neat :slight_smile:

Thanks for sharing. However, I think it's a different idiom than a local variable because the scope of reuse is limited to the single expression where the value materialises. Indeed the thought process was that of trying to avoid creating a local variable for such a short scope.

Sure, but any language feature you could introduce here is going to be more awkward and less clear than just using a local variable.

I haven’t formed an opinion on the idea yet, but in the interest of steel-manning the motivation, what if a subscript is involved? Then you can’t just use a local, since you want the mutation to be realized in the original collection not a local copy of it:

someCollection[i] = aComplicatedExpression.involving(someCollection[i])

+1 on the local variable.

If I was really going to do this a lot I'd write an extension on Dictionary that takes the Device and uses Device.name as the key.

Terms of Service

Privacy Policy

Cookie Policy