Swift - Take Nil as Argument in Generic Function with Optional Argument

Swift - Take Nil as Argument in Generic Function with Optional Argument

I believe you've overcomplicated the problem by requiring the ability to pass untyped nil (which doesn't really exist; even nil has a type). While the approach in your answer seems to work, it allows for the creation of ?? types due to Optional promotion. You often get lucky and that works, but I've seen it blow up in really frustrating ways and the wrong function is called. The problem is that String can be implicitly promoted to String? and String? can be implicitly promoted to String??. When ?? shows up implicitly, confusion almost always follows.

As MartinR points out, your approach is not very intuitive about which version gets called. UnsafePointer is also NilLiteralConvertible. So it's tricky to reason about which function will be called. "Tricky to reason about" makes it a likely source of confusing bugs.

The only time your problem exists is when you pass a literal nil. As @Valentin notes, if you pass a variable that happens to be nil, there is no issue; you don't need a special case. Why force the caller to pass an untyped nil? Just have the caller pass nothing.

I'm assuming that somethingGeneric does something actually interesting in the case that it is passed nil. If that's not the case; if the code you're showing is indicative of the real function (i.e. everything is wrapping in an if (input != nil) check), then this is a non-issue. Just don't call somethingGeneric(nil); it's a provable no-op. Just delete the line of code. But I'll assume there's some "other work."

func somethingGeneric<T>(input: T?) {
somethingGeneric() // Call the base form
if (input != nil) {
print(input!);
}
}

func somethingGeneric() {
// Things you do either way
}

somethingGeneric(input: "Hello, World!") // Hello, World!
somethingGeneric() // Nothing

Swift Optional Generics Type required even though parameter is nil

Creating another overload is the way to do it. Swift doesn't have variadic generics yet. Until then, you'll need an overload for each number of placeholders. And zero is one of those numbers!

Typically, this means you'll be using a third (likely private) function for common functionality.

Note: it's impossible to use these default parameters, even though they compile!

private func common() { }

func ƒ() { common() }
ƒ()

func ƒ<T>(_: T? = nil) { common() }
ƒ( () )

func ƒ<T0, T1>(_: (T0, T1)? = nil) { common() }
ƒ(
( (), () )
)

Set default or nil value to the generic type parameter of the function

nil is too broad in this case, as the compiler can't infer to which [T]? it should apply the nil.

You will need to explicitly specify the Optional generic argument here:

self.genericCall(param: [StructOne]?.none)

Optional generic type doesn't accept nil in method call

Type inferring is not omniscient. When invoking a generic method, the compiler has to know the generic types you are using. Type inferring cannot see what type is nil supposed to be so you have to specify the types explicitly.

broadcastEvent(SettingsEvent.Baz, withValue: nil as NSString?)

Also note that String is a struct so it doesn't conform to AnyObject. Using a literal "aa" will make it a NSString.

I don't think you will be able to combine a generic type with a default parameter value of nil, only by defining a separate method

func broadcastEvent<E: Event>(event: E) {
broadcastEvent(event, withValue: nil as AnyObject?)
}

Returning a nil from an optional generic extension

I don't think you can achieve this with a static property, however you can achieve it with a static function:

extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}

let x: Result<Int?> = .nil()

Functions are way more powerful than properties when it comes to generics.


Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:

protocol OptionalType {
associatedtype Wrapped
}

extension Optional: OptionalType { }

extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}

let x: Result<Int?> = .nil

One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.

Issue about extending Optional with Generic Type in Swift

extension Optional {
func safeUnwrap(_ defaultValue: Wrapped) -> Wrapped {
switch self {
case let value?: return value
case nil: return defaultValue
}
}
}

Or even

extension Optional {
func safeUnwrap(_ defaultValue: Wrapped) -> Wrapped {
self ?? defaultValue
}
}

But as was pointed out, this is more wordy and less idiomatic than just using the ?? operator.

Is it possible for a Generic to be optional and nil?

Even nil can be cast into optional type which should suffice for you. If I understand your question correctly you want it like so:

func printDetail<SuccessType, ErrorType>(route: APIRouter, resultSuccess: SuccessType? = nil, resultError: ErrorType? = nil) where SuccessType: Codable {

Now if you try and call the following you will receive an error as you describe:

printDetail(route: route, resultSuccess: nil, resultError: nil) // Generic parameter 'ErrorType' could not be inferred

But if you simply cast your nil objects:

printDetail(route: route, resultSuccess: nil as String?, resultError: nil as String?)

All should work fine. This way you give your compiler a hint on types and that is all it needs.

Default value for optional generic parameter in Swift function

It's impossible in the way you've done it. Given just the code above, what type is T? The compiler, as it says, can't figure it out (neither can I, and I assume you couldn't either because the data's not there).

The solution to the specific question is to overload rather than use defaults:

func addChannel<T>(name: String, data: T?) -> Channel { ... }

func addChannel(name: String) -> Channel { ... }

let myChannel = addChannel("myChannelName")

But it raises the question of what you're doing here. You would think that Channel should be Channel<T>. Otherwise, what are you doing with data? Without resorting to Any (which you should strongly avoid), it's hard to see how your function can do anything but ignore data.

With Channel<T> you can just use a default, but you'd have to provide the type:

func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
let myChannel: Channel<Int> = addChannel("myChannelName")

Otherwise the compiler wouldn't know what kind of channel you're making.

(UPDATE ~ Swift 5.2)

Sometimes you'd like a default type for T. You can do that with an overload. For example, you might want the default type to be Never. In that case, you would add an overload like this:

func addChannel<T>(name: String, data: T? = nil) -> Channel<T> { ... }
func addChannel(name: String) -> Channel<Never> {
addChannel(name: name, data: Optional<Never>.none)
}

With that, you can have a simpler call:

let myChannel = addChannel(name: "myChannelName") // Channel<Never>


Related Topics



Leave a reply



Submit