Skip to content

Instantly share code, notes, and snippets.

@gsheld
Last active July 22, 2016 00:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gsheld/542a57c1ca5efe87892a28759215283e to your computer and use it in GitHub Desktop.
Save gsheld/542a57c1ca5efe87892a28759215283e to your computer and use it in GitHub Desktop.
Example: Struct syntax for requesting a copy with an ivar set to an explicit value
struct Foo {
var bar = 0
var baz: String
}
let foo = Foo(bar: 1, baz: "baz") // Initial struct instance
let fooWithNewBar = foo.withBar(2) // Returns a new `Foo` with `bar` set to 2; `baz` remains set to "baz".
let fooWithNewBaz = foo.withBaz("BAZ") // Returns a new `Foo` with `baz` set to "BAZ"; `bar` remains set to 1.
let fooWithChaining = foo.withBar(3) // Example of method chaining using this approach. `bar` is set to 3 and
.withBaz("BaZ") // `baz` is set to "BaZ".
// Note that both `bar` and `baz` are var (mutable) properties. Although it is possible in certain situations to create
// a copy of an object with a let (constant) property** set to a certain value (e.g. when the value of the let property is
// directly injected into the initializer), it would be impossible to adapt the .withX() syntax to lets in all situations.
// The .withX() syntax proposed above is a simple syntactic sugar. The same affect can be achieved using
// already-available swift syntax:
struct Foo {
var bar = 0
var baz: String
func withBar(bar: Int) -> Foo {
var newFoo = self
newFoo.bar = bar
return newFoo
}
func withBaz(baz: String) -> Foo {
var newFoo = self
newFoo.baz = baz
return newFoo
}
}
// Of course, the downside of this approach is the additional boilerplate required to generate these immutable "setters".
// It would require the enterprising developer to write similar code for *all* structs for which they would like to use
// this syntax. It would also force developers to extend those Structs for which they do not have the source code (e.g.
// String).
// Given Swift's focus on immmutability as evidenced by the design of Struct (e.g. the keyword "mutating" is explicitly
// required to achieve mutability in any Struct property or method), I feel the syntax proposed above would reduce the
// burden of developers interested in transitioning to more immutable-APIs. Of course, there is always the danger of
// overuse and potential for confusion with actual "setters" that do change the state of a particular instance. That is why
// feedback would be most welcome!
// ** The same logic can be applied to "private(set)" properties.
@gsheld
Copy link
Author

gsheld commented Jul 22, 2016

It's worth noting that there is another similar suggestion by Erica Sadun which involves syntax for method cascading.

A form of method cascading can also be achieved with vanilla Swift 2.2 --

// Here's an example using an overloaded operator.
infix operator ~> {}
func ~> <T>(lhs: T, rhs: (inout T) -> ()) -> T {
  var this = lhs
  rhs(&this)
  return this
}

let cascadeFoo = foo ~> {
  $0.bar = 2
  $0.baz = "BaZ"
}

For setting a single property, method cascading is not quite as elegant but is more general use than what I propose -- e.g. the syntax is valid with both methods and properties in addition to working on Class-based abstractions. Its an interesting alternative to the method chaining I briefly outline above, but their use cases do seem somewhat different.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment