Beginner’s question: Why can’t I print nil?

I tried to type print(nil) in the REPL. I got this error:

error: repl.swift:1:7: error: 'nil' is not compatible with expected argument type 'Any'
print(nil)

What’s going on here? Why is nil a type? And why is it not a subtype of Any (which sounds like it should be compatible with every type)?

Hm, is nil a type like nullptr_t in new C++ versions that just autocoerces to every T??

I guess it's more like nullopt/nullopt_t?

nil by itself has no type. You need some context to give it a type (try print(nil as String?), that should work just fine). This gets easier to understand with experience, but no less frustrating.

1 Like

Does the nil in the error message refer to the expression nil or to some type also called nil?

It refers to the value. It would say "of type" if it was referring to a type, e.g:

cannot convert value of type 'Int' to expected argument type 'String'

1 Like

Thanks!

To add to this, and to be a bit more precise, nil isn't a value, but a literal (like integer and string literals), and can be used to construct a concrete value of any type conforming to ExpressibleByNilLiteral (of which the most common is Optional).

In this case, print(_:separator:terminator:) takes an Any..., and Any does not conform to ExpressibleByNilLiteral, so there's no value that could be constructed which would be valid to call with.

You get this behavior for any function which takes a non-ExpressibleByNilLiteral type and try to pass in nil:

func ƒ(_ value: String) {}
ƒ(nil) // ❌  'nil' is not compatible with expected argument type 'String' 
3 Likes

There's some inconsistency here as in some contexts Optional.none works (without specifying any contextual type) and nil doesn't (below orange diamond looks an odd one as the bottom let a & b are somewhat inconsistent with the above green pairs). Although ... not a big deal, IMHO:

print("\(type(of: []))")            // ✅ Array<Any>
print("\(type(of: [:]))")           // ✅ Dictionary<AnyHashable, Any>
print("\(type(of: Optional.none))") // 🔶 Optional<Any>    Expression implicitly coerced from 'Optional<Any>' to 'Any'
print("\(type(of: nil))")           // 🛑 'nil' is not compatible with expected argument type 'Any'

print("\([])")                      // ✅ []
print("\([:])")                     // ✅ [:]
print("\(Optional.none)")           // 🛑 Generic parameter 'Wrapped' could not be inferred
print("\(nil)")                     // 🛑 'nil' is not compatible with expected argument type 'any Any.Type'

print([])                           // ✅ []
print([:])                          // ✅ [:]
print(Optional.none)                // 🛑 Generic parameter 'Wrapped' could not be inferred
print(nil)                          // 🛑 'nil' is not compatible with expected argument type 'any Any.Type'

print([].description)               // ✅ []
print([:].description)              // ✅ [:]
print(Optional.none.description)    // 🛑 Generic parameter 'Wrapped' could not be inferred
print(nil.description)              // 🛑 'nil' requires a contextual type

let a = []                          // 🛑 Empty collection literal requires an explicit type
let b = [:]                         // 🛑 Empty collection literal requires an explicit type
let c = Optional.none               // 🛑 Generic parameter 'Wrapped' could not be inferred
let d = nil                         // 🛑 'nil' requires a contextual type
3 Likes

Don't quote me on this, but I wouldn't be surprised if this comes down to the fact that type(of:) is implemented in the type checker, and ends up following different code paths to deduce the input type, leading to resolving Optional as Optional<Any> instead of erroring in the same way.

1 Like