0

I've been porting code back and forth between Swift and Kotlin. Inspired by this answer, I've been using ?.let { } ?: run {} as replacements for the many places I use if let ... { } else {} in Swift.

I've really come to like the Kotlin expression though. In my opinion it's more general than the keyword specific if let concoction. So for parity's sake--or at least for the sake of learning by trying silly experiments--I thought I'd try to implement the let construct in Swift.

Because let is a keyword in Swift, I discovered right away that I'd need to put backticks, or use a different name, like cull. That led to this uncompilable code:

extension Any {
    func cull<R>(block:(Self) -> R) -> R {
        return block(self)
    }
}

which given my apprentice level skills, probably has numerous problems. But most importantly, I'm informed that I can't extend Any (which is explained in better detail here).

So Any can't be extended. Is there any other language construct I can use to make it so that I can implement something like Kotlin's let? Basically:

<expression>.foo { <expressionResult> in ... }

As a bonus/aside question, I think even if there is a way (I do hope there is just so I can learn), it won't be the same, because the Kotlin one can be used for control flow because I can do a non local return from the Kotlin one, but not from a Swift closure. Is that also correct?

CLARIFICATION

Basically, I can implement the Kotlin let pattern for designated types:

protocol Cullable {}

extension Cullable {
    func cull<R>(_ block:(Self) -> R) -> R {
        return block(self)
    }
}

extension Int:Cullable { }

42.cull { thing in print(thing) } // this will print 42

This works as I want. But I don't want to have to extend every single non-nominal type in the system to make it work. I just want to define it globally some how. If it was possible.

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • It's not possible, because there is no way to have the "foo" part captured and passed into whatever function you concoct. `if let` is a language-feature, not a library feature – Alexander Aug 05 '18 at 00:38
  • See `CLARIFICATION` @Alexander. – Travis Griggs Aug 05 '18 at 00:54
  • 1
    Oh, I see. Just write an extension on `Optional`, it's not much different. – Alexander Aug 05 '18 at 02:33
  • Can you give an example of what you are actually trying to accomplish? Like give the Kotlin code you are trying to replicate in Swift? I'm a little confused because the ?.let in Kotlin is actually, in my opinion, a much worse option than what Swift has. In most cases, I'd much rather have a Swifty guard let or if let than a Kotlin ?.let. – TJ Olsen Oct 24 '22 at 14:22

1 Answers1

5

Actually, I think such a method already exists in Optional. Although this is only for optionals, at least it solves your problem of the lack of ?.let. The method is called map.

let optional: String? = "hello"
optional.map { print($0) } // prints hello
// note how I did not unwrap the optional. I called "map" directly on the optional, not the wrapped "String".

You can map the value to into another value, or Void, like I did above.

I don't think you can have a let method on everything.

flatMap is also available if you want to return an optional inside the closure.

And yes, after looking at what "non-local return" means in Kotlin, I don't think you can do the same in Swift. Those curly brace thingys are "closures". They are "closed", so you can't return the surrounding closure in an inner closure.

Sweeper
  • 213,210
  • 22
  • 193
  • 313