Home > front end >  compactMap() closure fails when adding irrelevant NOP declaration?
compactMap() closure fails when adding irrelevant NOP declaration?

Time:04-13

Playground

XCode Version 13.3 (13E113)

Swift 5.6


First print of compactMap() closure displays this:

["What\'s", "Going", "On?"]

The second print displays this:

[(), (), ()]

Seems like if I declare anything inside the closure, the output of the closure changes.

print(  "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
    lineText
})

print(  "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
    let _ : String
    lineText
})

Is there a way to wrap some of it to hide the other declaration?

Is the closure confused about the type?

Is there any way to unconfuse it (or me)?

Is it a Swift bug?

Issue is root of why other things I'm trying to with this pattern aren't working.

UPDATE

As per @Shadowrun's answer below, I added a return statement in the 3rd example, but that leads to compile time errors. So is that resolvable?

print(  "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap
{ idx, lineText in
    let _ : String
    return lineText
})

expression failed to parse: error: test playground.playground:38:52: error: generic parameter 'ElementOfResult' could not be inferred print( "What's\nGoing\nOn?".split(separator:"\n").enumerated().compactMap

                                       ^
Swift.Sequence:2:28: note: in call to function 'compactMap'
    @inlinable public func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]</b>

CodePudding user response:

If you have a one line closure there's an implicit return, so the first one is returning lineText which is of type string. See "Functions With an Implicit Return" at https://docs.swift.org/swift-book/LanguageGuide/Functions.html

The second one doesn't actually return anything, once it is more than one line, there is no implicit return. So the return type is Void which is also spelled as the empty tuple, () and that's what you get there.

You need to say return lineText explicitly if you mean to return something.

This function:

{ idx, lineText in
    let _ : String
    lineText  // This line evaluates the value of lineText and does nothing with the result
}

does not return a value. Any function that doesn't return a value returns a Void value. Void is a type with only one possible value, called (). Mapping to void is a pointless thing to do. Even if your function did a side effect, like printing something, it wouldn't be good style to use map just for side effects.

You can guard against this kind of mistake by being more explicit about the return type in the closure

{ idx, lineText -> String in ... }
  • Related