Swift polymorphic closure dispatch with struct/protocols - swift

I have a case where I want to register either one argument or no argument closures with a service. There's always an argument available, but for brevity, I want to be able to register no arg closures as well, and then just dispatch the closure without the available argument in that case. Coming from a strong OO and dynamic types background where we love polymorphic dispatch and class inheritance trees and let the types figure themselves out, I can throw the following together:
class AbstractAction<T> {
func publish(value:T) {
fatalError("you should override this")
}
}
class NullaryAction<T>: AbstractAction<T> {
var closure:() -> ()
override func publish(_:T) {
closure()
}
init(closure:()->()) {
self.closure = closure
}
}
class UnaryAction<T>: AbstractAction<T> {
var closure:(T) -> ()
override func publish(value:T) {
closure(value)
}
init(closure:(T)->()) {
self.closure = closure
}
}
var action:AbstractAction = UnaryAction<Int>(closure: { print("\($0)") })
action.publish(42)
action = NullaryAction<Int>(closure: { print("something happened") } )
action.publish(42)
So I see 42 followed by something happened in my console. Great.
But I'd like to explore doing this with struct and/or enum. Value semantics are all the rage. The enum approach was relatively straightforward, I think:
enum Action<T> {
case Nullary( ()->() )
case Unary( (T)->() )
func publish(value:T) {
switch self {
case .Nullary(let closure):
closure()
case .Unary(let closure):
closure(value)
}
}
}
var action = Action.Unary({ (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = Action<Int>.Unary( { print("shorthand too \($0)") } )
action.publish(42)
action = Action<Int>.Nullary({ print("something happened") })
action.publish(42)
To do a struct approach, I it is my understanding that I should use a protocol to capture common interface of publish(value:T). But that's where things get confusing, because protocols apparently can't be mixed with generics? I tried:
struct NullaryAction<T> {
typealias ValueType = T
var closure:() -> ()
}
struct UnaryAction<T> {
typealias ValueType = T
var closure:(T) -> ()
}
protocol Action {
typealias ValueType
func publish(value:ValueType)
}
extension NullaryAction: Action {
func publish(_:ValueType) {
self.closure()
}
}
extension UnaryAction: Action {
func publish(value:ValueType) {
self.closure(value)
}
}
var action:Action = UnaryAction(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
This just produces a lot of errors at the bottom. I had tried to do the extensions as generics (e.g. extension NullaryAction<T>:Action), but it told me that T was unused, even though I had placed the typealias expressions in the extensions.
Is it possible to do this with struct/protocol? I'm happy with the enum solution, but was disappointed I couldn't realize it with the struct/protocol approach.

Judging by the fact that you want to cast your structs to their protocols (by using var action: Action = UnaryAction {...}), I'm assuming you don't need the publish method to have the right signature upon evoking it.
In other words, by declaring your Action protocol with a typealias, the compiler is expecting to specialise the publish method for each instance of your structs.
This means that you have two options:
Remove the type casting:
Example:
var action /*removed the :Action type casting */ = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
var anotherAction = NullaryAction<Int>(closure:{ print("something happened") }) //need to use another variable for this last one
anotherAction.publish(42)
This solution makes your publish methods also have the same signature that your structs have. If your struct is specialised to work with Ints, then you'll have .publish(value: Int).
Make the protocol non-generic
Example:
protocol Action {
func publish(value:Any)
}
struct NullaryAction<T>: Action {
let closure: () -> ()
init(closure: () -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure()
}
}
struct UnaryAction<T>: Action {
let closure: (T) -> ()
init(closure: (T) -> ()) {
self.closure = closure
}
func publish(value:Any) {
self.closure(value as! T) //Need to type cast here
}
}
var action: Action = UnaryAction<Int>(closure: { (arg:Int) -> () in print("\(arg)") })
action.publish(42)
action = UnaryAction<Int>(closure: { print("shorthand too \($0)") } )
action.publish(42)
action = NullaryAction<Int>(closure:{ print("something happened") })
action.publish(42)
This solution allows you to keep on type casting, but your publish methods will all have the same signature (.publish(value: Any)). You also need to account for that upon executing the closure.

Related

Wrap a higher-order function in some context and apply it

Similar, I believe, to an applicative, I would like to wrap a higher-order function in some context and apply it to a sequence.
protocol Foo { func a() -> Void }
class Bar: Foo { func a() { } }
let seq = [Bar(), Bar(), Bar()]
Concretely, Give the above three definitions, I'd like to be able to call dispatch(event) where dispatch wraps up a forEach over a sequence of protocol instances, and event is a function defined by that protocol.
private func dispatch(event: () -> Void) -> Void {
DispatchQueue.main.async { // context that the forEach mapping should happen inside of
seq.forEach($0.event())
}
}
let _ = dispatch(Foo.a)
Obviously, this doesn't work with Swift's type system (I'm used to Clojure's apply()). As a possible alternative, is there a way to wrap the sequence into a partial that I can forEach on?
let dispatch() -> [Foo] {
DispatchQueue.main.async {
seq.forEach // 🤷🏻‍♂️
}
}
let _ = dispatch { $0.a() }
Perhaps dispatch should be thought of as a constrained extension to Sequence?
extension Sequence where Iterator.Element == Foo {
func dispatch() -> [Foo] {
DispatchQueue.main.async {
return self.forEach // 🤷🏾‍♀️
}
}
}
While not as elegant as could have been with key paths to instance methods, you could also create a dispatch function that takes in a closure with each element as its parameter:
func dispatch(_ handler: #escaping (Foo) -> Void) {
DispatchQueue.main.async {
seq.forEach(handler)
}
}
And invoke it like so:
dispatch { $0.a() }
While you can't use KeyPath for instance methods, you can use it for properties, if it works for you.
You can change your protocol and implementation to something like this:
protocol Foo {
var a: () -> Void { get }
}
class Bar: Foo {
lazy var a = {
print("Bar")
}
}
Then you can define your dispatch function to take a KeyPath as parameter:
extension Sequence where Iterator.Element: Foo {
func dispatch(keyPath: KeyPath<Foo, () -> Void>) {
DispatchQueue.main.async {
forEach { $0[keyPath: keyPath]() }
}
}
}
and pass the properties KeyPath as parameter:
let seq = [Bar(), Bar(), Bar()]
seq.dispatch(keyPath: \Foo.a)

How can you store an array of protocols with associated types in Swift without using Any?

I'm looking to do something like this:
protocol StateType { }
struct DogState: StateType { }
struct CatState: StateType { }
protocol Action {
associatedType ST
func process() -> ST
}
struct DogAction {
func process() -> DogState { return DogState() }
}
struct CatAction {
func process() -> CatState { return CatState() }
}
let actions = [DogAction(), CatAction()]
actions.forEach {
$0.process()
}
I've tried fooling around with type-erasure but I can't get past the issue of not being able to create heterogeneous arrays.
struct AnyActionProtocol<A: Action, ST: StateType>: Action where A.StateType == ST {
let action: A
init(_ action: A) {
self.action = action
}
func process() -> ST {
return action.process()
}
}
let dap = AnyActionProtocol<DogAction, DogState>(DogAction())
let cap = AnyActionProtocol<CatAction, CatState>(CatAction())
When I try this, the error I get is Heterogeneous collection literal could only be inferred to [Any] which is something I'm trying to avoid.
Any help would be appreciated!
PATs are usually complex and do not involve an easier solution. I would suggest you come up with some simpler design for your problem. The problem with your method is actually having PAT and nested protocols and trying to make them work together.
Even if you type erase Action to some type like AnyAction, these two different types DogAction and CatAction again produce different types and you cannot intermix them inside the array. What you can possibly do is have two type erasures, one for Action and other for StateType. When you wrap CatAction or DogAction inside AnyAction both of them would then wrap in type erase state to AnyState.
Here is one example you could approach this type erasing both the protocols,
protocol StateType { }
struct DogState: StateType { }
struct CatState: StateType { }
protocol Action {
associatedtype ST: StateType
func process() -> ST
}
struct DogAction: Action {
func process() -> DogState { return DogState() }
}
struct CatAction: Action {
func process() -> CatState { return CatState() }
}
struct AnyState: StateType {
let state: StateType
init(_ state: StateType) {
self.state = state
}
}
struct AnyAction: Action {
typealias ST = AnyState
let processor: () -> AnyState
init<T: Action>(_ a: T) {
self.processor = {
return AnyState(a.process())
}
}
func process() -> AnyState {
return processor()
}
}
let cat = AnyAction(CatAction())
let dog = AnyAction(DogAction())
let actions = [cat, dog]
actions.forEach { action in
action.process()
}
I still think that you should rethink your solution, this can get more complicated as your types increase.

swift - Pass generic type to method with more specific extension requirements

So the title is a little weirdly worded, but here is the basis of what I am looking to do. I want to make a function that can determine if the generic type given extends from a specific protocol and then pass through the type to the more specific method for processing. This would be using the swift programming language to do so.
Psuedo code of what I want to achieve below:
func doStuff<T>(callback: Callback<T>) {
// Pseudo code of what I want to achieve as I'm not sure the syntax
// nor if it's even possible
if T extends Protocol {
let tExtendsProtocolType = T.Type as Protocol
mapStuffSpecific<tExtendsProtocolType>(callback: callback)
} else {
// Standard Use Case
}
}
func doStuffSpecific<T: Protocol>(callback: Callback<T> {
}
Thanks in advance
EDIT 1
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
// I want to be able to use this to do some common set up then call into either doStuff<T> or doStuff<T: Protocol>
func tryDoStuff<T>(callback: Callback<T>) {
// Do some common setup then call this
doStuff(callback: callback)
}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
tryDoStuff(callback: callbackA) // prints doStuff
tryDoStuff(callback: callbackB) // prints doStuffSpecific
Swift's overload resolution algorithm already prioritizes the most specific overload available. Here's an example:
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
doStuff(callback: callbackA) // prints doStuff
doStuff(callback: callbackB) // prints doStuffSpecific

Implementing Swift protocol with a constrained type parameter

I have a couple of Swift protocols that describe a general interface that I'm trying to implement in multiple ways:
protocol Identifiable
{
var identifier:String { get }
}
protocol ItemWithReference
{
var resolveReference<T:Identifiable>(callback:(T) -> ())
}
Now I want to implement the ItemWithReference protocol using CloudKit as the back end (this will eventually work with an alternate back-end as well, at which time I expect to provide an alternative implementation of the ItemWithReference protocol.
In my CloudKit implementation, I have something like this:
class CloudKitIdentifiable : Identifiable
{
...
}
class CloudKitItemWithReference : ItemWithReference
{
func resolveReference<T:Identifiable>(callback:(T) -> ())
{
// In this implementation, I want to only proceed if `T` is a CloudKitIdentifiable subtype
// But not sure how to enforce that
}
}
What I would like to do is to constrain T to be a CloudKitIdentifiable rather than just a simple Identifiable. I can't do that directly in the resolveReference declaration because then the function wouldn't conform to the ItemWithReference protocol. So instead, I am hoping to confirm that T is indeed a CloudKitIdentifiable and then invoke it's initializer to create a new instance of the class being resolved.
Is there any way in Swift to use T's metatype T.Type and determine if it is a subtype of another type? Furthermore, is there any way to invoke a required initializer that has been declared on that subtype?
try:
class CloudKitIdentifiable : Identifiable {
var identifier:String = ...
required init() {}
// you need `required`.
}
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if T.self is CloudKitIdentifiable.Type {
// do work..
let obj = (T.self as CloudKitIdentifiable.Type)()
callback(obj as T)
}
}
}
OR:
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if let CKT = T.self as? CloudKitIdentifiable.Type {
// do work..
let obj = CKT()
callback(obj as T)
}
}
}
But, In this case, you have to call resolveReference like this:
let ref = CloudKitItemWithReference()
ref.resolveReference { (obj: CloudKitIdentifiable) -> () in
// ^^^^^^^^^^^^^^^^^^^^ explicit type is necessary.
println(obj.identifier)
return
}
Rathar than that, I would recommend to use Associated Type:
protocol Identifiable {
var identifier:String { get }
}
protocol ItemWithReference {
typealias Item: Identifiable // <-- HERE is associated type
func resolveReference(callback:(Item) -> ())
}
class CloudKitIdentifiable : Identifiable {
var identifier:String
init(identifier: String) {
self.identifier = identifier
}
}
class CloudKitItemWithReference : ItemWithReference {
// `Item` associated type can be inferred from
// the parameter type of `resolveReference()`
//
// typealias Item = CloudKitIdentifiable
func resolveReference(callback:(CloudKitIdentifiable) -> ()) {
let obj = CloudKitIdentifiable(identifier: "test")
callback(obj)
}
}
let ref = CloudKitItemWithReference()
ref.resolveReference { obj in
println(obj.identifier)
return
}

Anonymous class in swift

Is there an equivalent syntax or technique for Anonymous class in Swift?
Just for clarification Anonymous class in Java example here - http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
Thanks!
There is no equivalent syntax, as far as I know.
Regarding equivalent techniques, theoretically you could use closures and define structs and classes inside them. Sadly, I can't get this to work in a playground or project without making it crash. Most likely this isn't ready to be used in the current beta.
Something like...
protocol SomeProtocol {
func hello()
}
let closure : () -> () = {
class NotSoAnonymousClass : SomeProtocol {
func hello() {
println("Hello")
}
}
let object = NotSoAnonymousClass()
object.hello()
}
...currently outputs this error:
invalid linkage type for global declaration
%swift.full_heapmetadata* #_TMdCFIv4Test7closureFT_T_iU_FT_T_L_19NotSoAnonymousClass
LLVM ERROR: Broken module found, compilation aborted!
Command /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 1
You can also create a basic empty class that acts like a bare protocol, and pass a closure to the init function that overrides anything you want, like this:
class EmptyClass {
var someFunc: () -> () = { }
init(overrides: EmptyClass -> EmptyClass) {
overrides(self)
}
}
// Now you initialize 'EmptyClass' with a closure that sets
// whatever variable properties you want to override:
let workingClass = EmptyClass { ec in
ec.someFunc = { println("It worked!") }
return ec
}
workingClass.someFunc() // Outputs: "It worked!"
It is not technically 'anonymous' but it works the same way. You are given an empty shell of a class, and then you fill it in or override whatever parameters you want when you initialize it with a closure.
It's basically the same, except instead of fulfilling the expectations of a protocol, it is overriding the properties of a class.
For example, Java listener/adapter pattern would be translated to Swift like this:
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Adapter : EventListener {
func handleEvent(event: Int) -> () {
}
}
var instance: EventListener = {
class NotSoAnonymous : Adapter {
override func handleEvent(event: Int) {
println("Event: \(event)")
}
}
return NotSoAnonymous()
}()
instance.handleEvent(10)
(Crashing the compiler on Beta 2)
The problem is, you always have to specify a name. I don't think Apple will ever introduce anonymous classes (and structs etc.) because it would be pretty difficult to come with a syntax that doesn't collide with the trailing closures.
Also in programming anonymous things are bad. Naming things help readers to understand the code.
No anonymous class syntax in Swift. But, you can create a class inside a class and class methods:
class ViewController: UIViewController {
class anonymousSwiftClass {
func add(number1:Int, number2:Int) -> Int {
return number1+number2;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
class innerSwiftClass {
func sub(number1:Int, number2:Int) -> Int {
return number1-number2;
}
}
var inner = innerSwiftClass();
println(inner.sub(2, number2: 3));
var anonymous = anonymousSwiftClass();
println(anonymous.add(2, number2: 3));
}
}
This is what I ended up doing (Observer pattern). You can use closures in a similar way you would use anonymous classes in Java. With obvious limitations of course.
class Subject {
// array of closures
var observers: [() -> Void] = []
// #escaping says the closure will be called after the method returns
func register(observer: #escaping () -> Void) {
observers.append(observer)
}
func triggerEvent() {
observers.forEach { observer in
observer()
}
}
}
var subj = Subject()
// you can use a trailing closure
subj.register() {
print("observerd")
}
// or you can assign a closure to a variable so you can maybe use the reference to removeObserver() if you choose to implement that method
var namedObserver: () -> Void = {
print("named observer")
}
subj.register(observer: namedObserver)
subj.triggerEvent()
// output:
// observerd
// named observer
If you want to inline a click handler in Java style fashion, first define your closure as a variable in your button class:
var onButtonClick: () -> Void = {}
Then add a method to accept the closure as parameter and store it in the variable for later use:
func onClick(label: String, buttonClickHandler: #escaping () -> Void) {
button.label = label
onButtonClick = buttonClickHandler
}
Whenever the closure should be executed, call it in your button class:
onButtonClick()
And this is how to set the action that should occur on click:
newButton.onClick(label: "My button") { () in
print("button clicked")
}
You can also accept multiple parameters. For example, a toggle button may be handled like this:
var buttonClicked: (_ isOn: Bool) -> Void { set get }
Simply use a struct for defining the interface via function values and then anonymously implement it from a function, as is a very common way to write objects in JavaScript.
The function is only required for creating a private scope for the object returned.
import Foundation
struct Logger {
let info: (String) -> ()
let error: (String) -> ()
}
func createSimpleLogger() -> Logger {
var count = 0
func number() -> Int {
count += 1
return count
}
return Logger(
info: { message in
print("INFO - #\(number()) - \(message)")
},
error: { message in
print("ERROR - #\(number()) - \(message)")
}
)
}
let logger = createSimpleLogger()
logger.info("Example info log message")
logger.error("Example error log message")
Output:
INFO - #1 - Example info log message
ERROR - #2 - Example error log message