Last active
February 3, 2018 23:20
-
-
Save rnapier/5c80d113895a796fc17b to your computer and use it in GitHub Desktop.
Wrapping funcs into protocols
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*: | |
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. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Shouldn’t the implemenation of
draw()
in line 32 be this:instead of
func draw(renderer: Renderer) { self.draw(renderer) }