How can I create an operator to implement error chaining?

谁说我不能喝 提交于 2020-07-16 10:48:24

问题


I want to implement the following operator »:

throwingFunction(arg: T?)».doStuff()

/*  if throwingFunction throws an error:
     print or log the error
  else 
    returns an object having the doStuff() Method 

OR 

 An Alternative design that I'm open to is 
Instead of a throwing Error, the `throwingFunction()` 
can be swapped out for a method that returns `Result` 

OR
 
a custom Type, with a generic payload type.   
*/

Here is an example of something similar to what I'm looking for. It is a custom implementation of optional chaining made possible using the KeyPath object (credit to Sergey Smagleev ).

precedencegroup Chaining {
    associativity: left
}

infix operator ~> : Chaining

extension Optional {
  
  static func ~><T>(value: Wrapped?, key: KeyPath<Wrapped, T> ) -> T? {
    return value.map { $0[keyPath: key] }
  }
  
  static func ~><T>(value: Wrapped?, key: KeyPath<Wrapped, T?> ) -> T? {
    return value.flatMap { $0[keyPath: key] }
  }
  
}

struct Object {
    let anotherOptionalObject: AnotherObject?
}

struct AnotherObject {
    let value: String
}

let optionalObject: Object? = Object(anotherOptionalObject: AnotherObject(value: "Hello world"))

print(optionalObject~>\.anotherOptionalObject~>\.value) //this prints Optional("Hello world")
print(optionalObject?.anotherOptionalObject?.value) //this also prints Optional("Hello world")

Except, I want the implementation to provide an opportunity for me to handle the error by printing or logging it.


回答1:


prefix and postfix are unary operators i.e. they only accept one operand, whereas an infix operator is a binary operator i.e. it accepts two operands.

So, static func »(value:key:) -> Preferred? is not correct because it's taking two operands whereas you have defined » as a postfix operator.




回答2:


For some reason I get "» is not a postfix unary operator" when I don't use the escape syntax. But apart from that it seems to work.

precedencegroup Chaining {
    associativity: left
}

infix operator » : Chaining

extension Result {
    static func »<T>(value: Self, key: KeyPath<Success, T>) -> T? {
        switch value {
        case .success(let win):
            return win[keyPath: key]
        case .failure(let fail):
            print(fail.localizedDescription)
            return nil
        }
    }
}

// I included a custom type so that it could be customizable if needed.  
enum Err<Wrapped> {
    static func »<T>(value: Self, key: KeyPath<Wrapped, T>) -> T? {
        switch value {
        case .error(let err):
            print(err.localizedDescription)
            return nil
        case .some(let wrapper):
            return wrapper[keyPath: key]
        }
    }
    case error(Error)
    case some(Wrapped)
}

func errorWrapped() -> Err<String> {
    .some("Hello World")
}

func pleaseWork()  {
    print(errorWrapped()»\.isEmpty)
}


来源:https://stackoverflow.com/questions/62722764/how-can-i-create-an-operator-to-implement-error-chaining

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!