# Conditionally conform to Sendable when closure property is Sendable

**URL:** <https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629>\
**Category:** Using Swift\
**Tags:** concurrency, closure, escapingclosure\
**Created:** [March 13, 2024, 1:36am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629 "2024-03-13T01:36:21Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![roanutil](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roanutil/32/12060_2.png) [@roanutil](https://forums.swift.org/u/roanutil)\
**Post date:** [March 13, 2024, 1:36am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/1 "2024-03-13T01:36:21Z")

</div>

My understanding is that closures are treated a little differently and can't conditionally be `Sendable`.

Let's say I have a type that holds onto a closure:

```swift
public struct Effect<Input, Output> {
    public typealias Closure = (Input) -> Output
    
    public let closure: Closure
    
    public init(closure: @escaping Closure) {
        self.closure = closure
    }
}

```

This will work until I need it to be `Sendable`. It's easy enough to conditionally conform the type to `Sendable` when `Input` and `Output` are `Sendable` like so:

```swift
extension Effect: Sendable where Closure: Input: Sendable, Output: Sendable {}

```

However, this will correctly result in a warning because `Closure` isn't `Sendable`. What if we tried to conditionally conform `Closure` to `Sendable` to see what happens...

```swift
extension Effect: Sendable where Closure: Sendable, Input: Sendable, Output: Sendable {}

```

This will compile but the warning remains because I guess a function requires the `@Sendable` annotation instead of the protocol conformance.

Is there any way to handle cases like this without making a separate type like below?

```swift
public struct SendableEffect<Input, Output>: Sendable {
    
    public let closure: @Sendable (Input) -> Output
    
    public init(closure: @escaping @Sendable (Input) -> Output) {
        self.closure = closure
    }
}

```

---

<div class="post-metadata">

**Author:** ![hgraves](https://avatars.discourse-cdn.com/v4/letter/h/ec9cab/32.png) [@hgraves](https://forums.swift.org/u/hgraves)\
**Post date:** [March 13, 2024, 1:48am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/2 "2024-03-13T01:48:17Z")

</div>

> This will work until I need it to be `Sendable` .

Can you provide an example snippet that requires Sendability

---

<div class="post-metadata">

**Author:** ![roanutil](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roanutil/32/12060_2.png) [@roanutil](https://forums.swift.org/u/roanutil)\
**Post date:** [March 14, 2024, 10:41pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/3 "2024-03-14T22:41:51Z")

</div>

Yes, but I won't be able to get to it for a few days

---

<div class="post-metadata">

**Author:** ![hgraves](https://avatars.discourse-cdn.com/v4/letter/h/ec9cab/32.png) [@hgraves](https://forums.swift.org/u/hgraves)\
**Post date:** [March 15, 2024, 7:06am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/4 "2024-03-15T07:06:11Z")

</div>

👍 ​

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [March 15, 2024, 9:00pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/5 "2024-03-15T21:00:12Z")

</div>

This just isn't how function types naturally interact with `Sendable`. The sendability of a function depends on its captures and is orthogonal to its parameter and result types. There's no reason a function that takes `Sendable` arguments can't be non-`Sendable` or vice-versa.

---

<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:** [March 15, 2024, 9:35pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/6 "2024-03-15T21:35:55Z")

</div>

If you could write `T: (Foo) -> Bar` you’d be able to make this conditional though. So it’s something Swift doesn’t expose in its surface language today (“no, there’s no way to do this”) but it’s not impossible to represent.

---

<div class="post-metadata">

**Author:** ![roanutil](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roanutil/32/12060_2.png) [@roanutil](https://forums.swift.org/u/roanutil)\
**Post date:** [March 15, 2024, 9:54pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/7 "2024-03-15T21:54:43Z")

</div>

Oops, I am now seeing that a function can be sendable while its parameters or return are not. Did not realize that.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [March 15, 2024, 11:17pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/8 "2024-03-15T23:17:56Z")

</div>

> [@jrose](#):
>
> If you could write `T: (Foo) -> Bar` you’d be able to make this conditional though. So it’s something Swift doesn’t expose in its surface language today (“no, there’s no way to do this”) but it’s not impossible to represent.

I don't think dependently-`Sendable` function types would be a useful feature. Generic code that just wants to pass function values around doesn't need to know that they're function values, and the sendability of the functions will be preserved by opaque generics in the usual way. Constraining the code to know that the values are functions is only useful if the code wants to call those values or produce new values from (say) a closure expression. In either case, not knowing whether the function type is `Sendable` would just force the code to be maximally conservative on both sides: you'd have to create `Sendable` closures but immediately treat them as non-`Sendable`.

In general, when you're working with a known function, you know how you plan to use it and whether those uses require it to be `Sendable`, so you should just take either a `Sendable` or a non-`Sendable` function as appropriate, with no need to be generic over its sendability.

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 16, 2024, 12:20am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/9 "2024-03-16T00:20:12Z")

</div>

> [@John\_McCall](#):
>
> I don't think dependently-`Sendable` function types would be a useful feature. Generic code that just wants to pass function values around doesn't need to know that they're function values, and the sendability of the functions will be preserved by opaque generics in the usual way. Constraining the code to know that the values are functions is only useful if the code wants to call those values or produce new values from (say) a closure expression. In either case, not knowing whether the function type is `Sendable` would just force the code to be maximally conservative on both sides: you'd have to create `Sendable` closures but immediately treat them as non-`Sendable`.

I'm a bit confused. How would a conditional conformance to `Sendable` based on whether a type parameter representing a function type is `Sendable` force the code to be maximally conservative / force you to create `@Sendable` function values? For example, pretend you could write this:

```swift
struct S<Arg, Result, Func> where Func == (Arg) -> Result {
  let fn: Func

  init(fn: Func) {
    self.fn = fn
  }

  func call(arg: Arg) -> Result {
    return fn(arg)
  }
}

extension S: Sendable where Func: Sendable {}

```

IMO this is a reasonable thing to want to do. We already let `@Sendable` function types be passed as generic arguments to a type parameter `T where T: Sendable`. That said, I'm not sure if it'd be tricky to teach the constraint system to infer closure types as `@Sendable` based on captures because today the constraint system doesn't reason about closure captures at all for the purpose of type inference; it only infers a closure type to be `@Sendable` if it's written contextually.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [March 16, 2024, 2:17am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/10 "2024-03-16T02:17:36Z")

</div>

If `Func` were actually constrained to “any function with this shape”, and you were in an extension of S, and you tried to turn a closure into a `Func`, you’d have to use a `Sendable` closure because `Func` _might_ be a sendable function type. You don’t get to assume that `Func` isn’t `Sendable` because `Func` isn’t statically constrained to `Sendable`; that’s not sound.

You’re right that there isn’t a similar constraint on calls, though. So yes, if you just want an opaque function value that you can pass around and call, you could reinvent it with this feature. Of course, you could also just use a function type.

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 16, 2024, 2:49am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/11 "2024-03-16T02:49:19Z")

</div>

I see, thanks! Also, my example can be expressed today like this:

```swift
// Compiled with -swift-version 6

struct Effect<Fn> {
  let fn: Fn

  init<Input, Result>(fn: Fn) where Fn == (Input) -> Result {
    self.fn = fn
  }

  func call<Input, Result>(arg: Input) -> Result where Fn == (Input) -> Result {
    fn(arg)
  }
}

extension Effect: Sendable where Fn: Sendable {}

@MainActor func use(effect: Effect<@Sendable (Int) -> Void>) {
  Task.detached {
    print(effect)
  }
}

@MainActor func bad(effect: Effect<(Int) -> Void>) {
  Task.detached {
    print(effect) // error: Capture of 'effect' with non-sendable type 'Effect<(Int) -> Void>' in a `@Sendable` closure
  }
}

```

Probably not worth it to actually write the code this way, but it does accomplish what I was after in my example.

---

<div class="post-metadata">

**Author:** ![roanutil](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/roanutil/32/12060_2.png) [@roanutil](https://forums.swift.org/u/roanutil)\
**Post date:** [March 19, 2024, 2:57am UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/12 "2024-03-19T02:57:39Z")

</div>

Thank you for the detailed response!

---

<div class="post-metadata">

**Author:** ![sliemeobn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sliemeobn/32/27698_2.png) [@sliemeobn](https://forums.swift.org/u/sliemeobn)\
**Post date:** [December 9, 2024, 3:36pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/13 "2024-12-09T15:36:43Z")

</div>

just for the "use case" record, I stumbled over this very thing as well in the context of my [HTML rendering library](https://github.com/sliemeobn/elementary/blob/e884367276ec18bced20c1492980817f54cb4134/Sources/Elementary/Core/ForEach.swift#L11).

```swift
public struct ForEach<Data, Content>: HTML
    where Data: Sequence, Content: HTML
{
    var sequence: Data

    // TODO: Swift 6 - @Sendable is not ideal here, but currently the response generators for hummingbird/vapor require sendable HTML types
    // also, currently there is no good way to conditionally apply Sendable conformance based on closure type
    var contentBuilder: @Sendable (Data.Element) -> Content

    public init(_ sequence: Data, @HTMLBuilder content contentBuilder: @escaping @Sendable (Data.Element) -> Content) {
        self.sequence = sequence
        self.contentBuilder = contentBuilder
    }

    public static func _render<Renderer: _HTMLRendering>(_ html: consuming Self, into renderer: inout Renderer, with context: consuming _RenderingContext) {
        // ...
    }
}

extension ForEach: Sendable where Data: Sendable {}

```

For most other DSL types I conform to `Sendable` conditionally based on their generic stored properties. But for the types that take a closure, I cannot really do that. Additionally, the generic trick Holly mentioned above does not really play well with result builders, and I could not get hand-crafted initializer overloads to work either.

With Swift 6 it's not thaaat dramatic, because I can use `sending` now and `HTML` types no longer really need to be sendable. (The TODO comment above predates Swift 6). But for 5.10 + concurrency checking it is not a great place to be in.

---

<div class="post-metadata">

**Author:** ![dnadoba](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dnadoba/32/11438_2.png) [@dnadoba](https://forums.swift.org/u/dnadoba)\
**Post date:** [December 9, 2024, 11:57pm UTC](https://forums.swift.org/t/conditionally-conform-to-sendable-when-closure-property-is-sendable/70629/14 "2024-12-09T23:57:31Z")

</div>

I think this would be quite useful for `LazySequence`/`LazyCollection` types that wrap a closure. Today they are all non-sendable (e.g. `[0, 1, 2].lazy.map { $0 * 2 }` is a non-sendable `LazyCollection`. They could be in theory conditionally Sendable, especially if the closure passed to map or one of the other transformations don't capture anything. This likely can't be changed for `LazySequence`/`LazyCollection` but we could think about it once we have the `Container` protocol that works with `Span`s.
