En Swift, estoy tratando de implementar un método "toque" similar al método que existe en Ruby.
Se me ocurrió el siguiente código de ejemplo:
private protocol Tap { mutating func tap(_ block: (inout Self) -> Void) -> Self } private extension Tap { mutating func tap(_ block: (inout Self) -> Void) -> Self { block(&self) return self } } extension Array: Tap {} var a = Array(repeating: "Hello", count: 5) a.tap { $0.append("5") }.tap { $0.append("7") } print(a) // (Expected) => ["Hello", "Hello", "Hello", "Hello", "Hello", "5", "7"] No estoy muy familiarizado con las funciones de mutación, los parámetros de entrada y salida o Swift en general, pero parece que el código anterior debería funcionar para mí. tap funciona como se esperaba cuando no se incluye en una cadena de métodos. Cuando lo incluyo como parte de una cadena de métodos, como en el ejemplo anterior, el compilador de Swift se queja:
No se puede usar un miembro mutante en un valor inmutable: la llamada a la función devuelve un valor inmutable
¿Alguien puede explicarme por qué esto no funciona? ¿Alguien puede proporcionar una solución funcional y explicar por qué funciona esa solución?
Otro ejemplo de uso sería:
let user = User(fromId: someId).tap { $0.firstName = someFirstName $0.lastName = someLastName } tap es una cosa de conveniencia que viene de Ruby. Estoy principalmente interesado en entender por qué los tipos en mi función no funcionan bien.
El return self devuelve una copia de la matriz original, no la matriz original en sí. Hasta que esta copia se almacene como var , no se puede mutar. Entonces, esto funcionaría:
var b = a.tap { $0.append("5") } b.tap { $0.append("7") } Pero no sin almacenar b como var primero. Por supuesto, no crearías una b en primer lugar, solo usarías a repetidamente como ya lo indicaste.
Entonces, el problema es que puede realizar un tap una vez, pero no puede encadenar tap . Esto se debe a que el retorno de self es implícitamente inmutable y no puede llamar a una función de mutación en un valor inmutable. Cambiar tap a una función sin mutación podría darte lo que quieres:
private extension Tap { func tap(_ block: (inout Self) -> Void) -> Self { let copy = self block(©) return copy } } var a = Array(repeating: "Hello", count: 5) a = a.tap({$0.append("5")}).tap({$0.append("7")}) Debido a que cada invocación de tap( devuelve una copia del original modificado por el bloque dado, puede llamarlo en tipos inmutables. Eso significa que puede encadenar.
El único inconveniente es que la nueva a = al principio.