# Should \`Result.map\` and related methods rethrow?

**URL:** <https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602>\
**Category:** Discussion\
**Tags:** throw, stdlib, api, result, typed-throws\
**Created:** [July 31, 2024, 4:51pm UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602 "2024-07-31T16:51:24Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![ellie20](https://avatars.discourse-cdn.com/v4/letter/e/6f9a4e/32.png) [@ellie20](https://forums.swift.org/u/ellie20)\
**Post date:** [July 31, 2024, 4:51pm UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/1 "2024-07-31T16:51:24Z")

</div>

The equivalent methods on other types, like `Array` and `Optional`, rethrow (propagate errors from their closure argument). Given that `Result` isn't supposed to be used as a replacement for throwing, I don't see a reason for why `Result`'s methods shouldn't rethrow.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [July 31, 2024, 5:53pm UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/2 "2024-07-31T17:53:39Z")

</div>

This was discussed in the original proposal(s) but I can’t recall the reasoning why they aren’t. I think it’s to avoid weirdness like `try Result {}.map {}`, as it seems like such a thing should just be captured into the `Result` value itself. Unfortunately `Result`’s API surface has never been expanded to make those operations nicer to do.

---

<div class="post-metadata">

**Author:** ![Quedlinbug](https://avatars.discourse-cdn.com/v4/letter/q/77aa72/32.png) [@Quedlinbug](https://forums.swift.org/u/Quedlinbug)\
**Post date:** [July 31, 2024, 7:07pm UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/3 "2024-07-31T19:07:02Z")

</div>

1. `rethrows` is deprecated for most use cases, including this one.
2. Unfortunately, [`Result.map`](https://developer.apple.com/documentation/swift/result/map(_:)) is named wrong. Its correct name is `mapSuccess`, and as you suggested, it, and what _should_ be named `map`, should be able to throw.

```swift
extension Result {
  func mapSuccess<NewSuccess, Error>(
    _ transform: (Success) throws(Error) -> NewSuccess
  ) throws(Error) -> Result<NewSuccess, Failure> {
    switch self {
    case .success(let success): .success(try transform(success))
    case .failure(let failure): .failure(failure)
    }
  }

  typealias Get = () throws(Failure) -> Success

  func map<NewSuccess, NewFailure, Error>(
    _ transform: (Get) throws(Error) -> Result<NewSuccess, NewFailure>
  ) throws(Error) -> Result<NewSuccess, NewFailure> {
    try transform(get)
  }

  func flatMap<NewSuccess, NewFailure>(
    _ transform: (Get) throws(NewFailure) -> NewSuccess
  ) -> Result<NewSuccess, NewFailure> {
    .init { () throws(_) in try transform(get) }
  }
}

```

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [July 31, 2024, 8:11pm UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/4 "2024-07-31T20:11:27Z")

</div>

> map is named wrong

It is consistent with Result in Rust, Elm, Kotlin, and Ocaml. Result is a success-biased type.

---

<div class="post-metadata">

**Author:** ![Quedlinbug](https://avatars.discourse-cdn.com/v4/letter/q/77aa72/32.png) [@Quedlinbug](https://forums.swift.org/u/Quedlinbug)\
**Post date:** [August 1, 2024, 9:08am UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/5 "2024-08-01T09:08:12Z")

</div>

I doubt that biasing ever makes sense when multiple types are involved. It leads to problems like we're seeing here, better demonstrated by Either's [map](https://apidocs.arrow-kt.io/arrow-core/arrow.core/-either/map.html) and [mapLeft](https://apidocs.arrow-kt.io/arrow-core/arrow.core/-either/map-left.html). 😵‍💫

What `Result` is missing is a full conversion function, either transforming a `Result`, or something functionally equivalent but more versatile, like the type of [`Result.get`](https://developer.apple.com/documentation/swift/result/get()), as I showed above. As it is, [the `flatMap`s](https://developer.apple.com/documentation/swift/result#Transforming-a-Result) allow transformations from successes to errors, and vice versa, within the same type, but not the much more useful transformations between two success types, and two error types, simultaneously. Or even just switching the error type based on the success value.

---

<div class="post-metadata">

**Author:** ![Quedlinbug](https://avatars.discourse-cdn.com/v4/letter/q/77aa72/32.png) [@Quedlinbug](https://forums.swift.org/u/Quedlinbug)\
**Post date:** [August 1, 2024, 9:10am UTC](https://forums.swift.org/t/should-result-map-and-related-methods-rethrow/73602/6 "2024-08-01T09:10:55Z")

</div>

> [@Jon\_Shier](#):
>
> I think it’s to avoid weirdness like `try Result {}.map {}`, as it seems like such a thing should just be captured into the `Result` value itself.

This is handleable via `map` and `flatMap`, because `Result` is just a cached version of its [`get`](https://developer.apple.com/documentation/swift/result/get()) signature.

```swift
enum Failure: Swift.Error { case notGood }
let boolResult = Result<_, Failure>.success(false)
let stringResult = "This is the mapped result."

let doesThrow = try boolResult.mapSuccess { success throws(Failure) in
  if success { stringResult } else { throw .notGood }
}
var doesntThrow = boolResult.mapSuccess { success in stringResult }
doesntThrow = boolResult.flatMapSuccess { success throws(Failure) in
  if success { stringResult } else { throw .notGood }
}

```

```swift
extension Result {
  func flatMapSuccess<NewSuccess>(
    _ transform: (Success) throws(Failure) -> NewSuccess
  ) -> Result<NewSuccess, Failure> {
    .init { () throws(_) in try transform(get()) }
  }
}

```
