`some` in closure parameters

I think I recently could not overcome this limitation.

I was thinking to make a nice wrapper for a simple case.

protocol Message {
    mutating func set(value: String) throws
}

protocol Proto {
    func enrich(_ message: inout some Message) throws
}

struct SimpleWrapper: Proto {
    let closure: (inout some Message) throws -> () // `- error: 'some' cannot appear in parameter position in result type '(inout some Message) throws -> ()'

    init(closure: @escaping ((inout some Message) throws -> ())) {
        self.closure = closure
    }
    func enrich(_ message: inout some Message) throws {
        try self.closure(&message)
    }
}

I would like to continue use some rather than any because if use any inout parameter can be replaced with other type...
I wonder if someone could help me finding a good solution.

I read some discussion here [Discussion] enable `some` in closure parameter position
and here: SE-0328: Structural opaque result types - #59
But unfortunately didn't find an answer...

Does this help?

2 Likes

The first key understanding to obtain is that generic functions cannot be stored as closures. You can only store specialized closures. I think all potential solutions will flow from that.

protocol Message {
  mutating func set(value: String) throws
}

protocol Proto {
  associatedtype Message: ModuleName.Message
  func enrich(_ message: inout Message) throws
}

struct SimpleWrapper<Message: ModuleName.Message>: Proto {
  let closure: (inout Message) throws -> Void
  
  init(closure: @escaping ((inout Message) throws -> Void)) {
    self.closure = closure
  }
  
  func enrich(_ message: inout Message) throws {
    try closure(&message)
  }
}
3 Likes

Thank you for the clarification.

I looked at the sample but unfortunately that doesn't suit my needs in full. I probably have to extend it a bit to provide the whole picture. I provide Proto as a part of configuration and use it into pluggable code.
Therefore the Proto doesn't have associatedtype Message and even doesn't know which types will be provided to it.

Currently I always just implement this Proto in client code:

// configuration
struct Configuration {
    let enricher: any Proto

    init(enricher: any Proto) {...}
}

// Client code usage:
struct MyEnricher: Proto {
    // ...
    func enrich(_ message: inout some Message) throws {
        message.set(...)
    }
}

Configuration(MyEnricher())

Backend code (plugin code) provides message:

extension MessageA: Message { ... }
extension MessageB: Message { ... }

struct Backend {
    let configuration: Configuration

    func sendMessageA() {
         var messageA = MessageA(...)
         try self.configuration.enrich(&messageA)
    }

    func sendMessageB() {
         var messageB = MessageB(...)
         try self.configuration.enrich(&messageB)
    }
}

This works good.

But as a next step I would like to simplify the client part to avoid always adding a new implementation of protocol for basic use-cases:

extension Configuration {
    init(enricher: @escaping (some inout Message) throws -> ()) {
        self.enricher = SimpleWrapper(enricher)
    }
}

I was trying to play around with some boxed classes but can't find something good so far.

Sorry, do you mean that this is not possible due to generic functions are not the "first-class"?

1 Like

Yes.