Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active February 3, 2018 23:20
Show Gist options
  • Save rnapier/5c80d113895a796fc17b to your computer and use it in GitHub Desktop.
Save rnapier/5c80d113895a796fc17b to your computer and use it in GitHub Desktop.
Wrapping funcs into protocols
/*:
Stuff from [Crustacean](https://developer.apple.com/sample-code/wwdc/2015/?q=protocol).
(Wish I could hide this in a single-file playground somehow.)
*/
import CoreGraphics
protocol Renderer {
func moveTo(position: CGPoint)
func lineTo(position: CGPoint)
func arcAt(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
}
protocol Drawable { func draw(renderer: Renderer) }
struct Diagram : Drawable {
func draw(renderer: Renderer) { for f in elements { f.draw(renderer) } }
mutating func add(other: Drawable) { elements.append(other) }
var elements: [Drawable] = []
}
/// START HERE
/*:
Let's say we don't have a full struct for this. We just have a `draw()` function.
Do I really need to create a named type for that every timea? Java's
anonymous classes may be a good solution here. (Did I just recommend a
feature from Java? Really?)
Instead, we can create a helper struct, but it's a bit heavy IMO.
*/
struct DrawFunc: Drawable {
let f: Renderer -> ()
func draw(renderer: Renderer) { self.draw(renderer) }
init(_ f: Renderer -> ()) { self.f = f }
}
var diagram = Diagram()
diagram.add(DrawFunc{$0.moveTo(CGPoint(x: 0, y:0))})
/*:
If properties were methods, then this would work, but instead we get
`does not conform to protocol 'Drawable'`. This is incredibly frustrating,
and a step backwards from ObjC, where properties and constructors *are*
methods.
*/
struct DrawFun: Drawable {
let draw: Renderer -> ()
}
diagram.add(DrawFun(draw: {$0.moveTo(CGPoint(x: 0, y:0))}))
/*:
Another approach would be like Go, to just add a method to the function.
This is really nice because it doesn't require a wrapper struct (so no
exta allocation or destruction. (UPDATE: [@jckarter notes that this is not true](https://twitter.com/jckarter/status/609393153179697152);
structs are the sum of their parts.) And it's very lightweight in code. But it
could be more awkward if you ever add another method to the protocol. That
said, this is one of those "wow, you can do that?" features of Go that
emerged naturally out of "you can put a method on anything." It's not used
in that many places, but it's very famously used in
[http.HandlerFunc](http://www.onebigfluke.com/2014/04/gos-power-is-in-emergent-behavior.html).
*/
extension (Renderer) -> Void : Drawable {
func draw(r: Renderer) { self(r) }
}
/*:
The problem here is that Swift has "nominal" and "non-nominal" types, and
functions are non-nominal, so they can't be extended. Swift has a lot of
these "yes, except" cases (it's why `Int` is surprisingly a `struct`).
To the earlier point, properties and initializers are not methods.
You can partially apply class methods, but not struct or protocol methods.
(UPDATE: [@jckarter notes that this is coming](https://twitter.com/jckarter/status/609393153179697152).)
Enum cases are not functions. You can pattern match some places, but not
others. Some things are expressions; some things are statements. There
are just lots and lots of little special cases.
That said, I suspect it's also related to why Swift is easier to optimize
than some languages. And it's lets Swift dodge some squirrely corner cases.
But dangit, I wish Swift were more regular.
*/
@ole
Copy link

ole commented Jun 16, 2015

Shouldn’t the implemenation of draw() in line 32 be this:

func draw(renderer: Renderer) { f(renderer) }

instead of func draw(renderer: Renderer) { self.draw(renderer) }

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