Calling similar Functions from different classes without protocols

Well my use case is a bit challenging and has to do with the way CoreML autogenerates a class for every model and a respective series of predict methods. I have done my best to boil down the problem to its simplest form.

I have the following two classes, each contains a predict method with ALWAYS has the same input type.

class one {
  func predict(input: Int) -> Double {
     // Code
  }
}

class two {
  func predict(cool: Int) -> Int {
     // Code
  }
}

The following class is responsible for calling the .predict method in the above classes and a generic apply function which allows me to invoke the function without knowing the parameter name.

class Consumer<Consumed> {

  let model: Consumed

  init(model: Consumed) {
    self.model = model
  }

  func anyFunction() {
    apply(fn: self.model.predict, arg: 4)
  }
}

// Generic Apply Method
func apply<T, V>(fn: (T) throws -> V, arg: T) -> V? {
  do {
    return try? fn(arg)
  }
}

Obviously the code above does not compile because I am attempting to call .predict on a generic type which is not guaranteed to have such a method. I believe the only way to fix this is to write a protocol which ensures the predict method exists in the generic paramter Consumed, but I am not sure how to do that. Below is my best attempt at the protocol that I believe <Consumed: ...> should conform to.

protocol ModelProtocol {
  func prediction<Input, Output>(input: Input) throws -> Output
}

Any help it appreciated, thanks!

Answered by OOPer in 681805022

Using a generic method as a protocol requirement may often be a mistake and you would find things would not work as you expect.

And predict(input:) and predict(cool:) are different methods, so you cannot define a single simple protocol. (And they do not throws.)

An example of defining the two classes and the protocol would be something like this:

class One {
    func predict(input: Int) -> Double {
        // Code
        return 0
    }
}

class Two {
    func predict(cool: Int) -> Int {
        // Code
        return 0
    }
}

protocol ModelProtocol {
    associatedtype Input: Numeric
    associatedtype Output
    func prediction(input: Input) throws -> Output
}

extension One: ModelProtocol {
    func prediction(input: Int) throws -> Double {
        predict(input: input)
    }
}

extension Two: ModelProtocol {
    func prediction(input: Int) throws -> Int {
        predict(cool: input)
    }
}

With above prepared, you can write something like this;

class Consumer<Consumed: ModelProtocol> {
    
    let model: Consumed
    
    init(model: Consumed) {
        self.model = model
    }
    
    func anyFunction() {
        _ = apply(fn: self.model.prediction(input:), arg: 4)
    }
}

// Generic Apply Method
func apply<T, V>(fn: (T) throws -> V, arg: T) -> V? {
    do {
        return try? fn(arg)
    }
}

Or, there may be some better ways, but with your simplified classes shown, I cannot say what would be better.

Accepted Answer

Using a generic method as a protocol requirement may often be a mistake and you would find things would not work as you expect.

And predict(input:) and predict(cool:) are different methods, so you cannot define a single simple protocol. (And they do not throws.)

An example of defining the two classes and the protocol would be something like this:

class One {
    func predict(input: Int) -> Double {
        // Code
        return 0
    }
}

class Two {
    func predict(cool: Int) -> Int {
        // Code
        return 0
    }
}

protocol ModelProtocol {
    associatedtype Input: Numeric
    associatedtype Output
    func prediction(input: Input) throws -> Output
}

extension One: ModelProtocol {
    func prediction(input: Int) throws -> Double {
        predict(input: input)
    }
}

extension Two: ModelProtocol {
    func prediction(input: Int) throws -> Int {
        predict(cool: input)
    }
}

With above prepared, you can write something like this;

class Consumer<Consumed: ModelProtocol> {
    
    let model: Consumed
    
    init(model: Consumed) {
        self.model = model
    }
    
    func anyFunction() {
        _ = apply(fn: self.model.prediction(input:), arg: 4)
    }
}

// Generic Apply Method
func apply<T, V>(fn: (T) throws -> V, arg: T) -> V? {
    do {
        return try? fn(arg)
    }
}

Or, there may be some better ways, but with your simplified classes shown, I cannot say what would be better.

Calling similar Functions from different classes without protocols
 
 
Q