[Pitch] Nil struct


(Anton Zhilin) #1

Gist link <https://gist.github.com/Anton3/ba56a29986c59e9595368be3cb02fb1b>
Introduction

Change nil literal type from () to Nil.
Before:

public protocol ExpressibleByNilLiteral {
  init(nilLiteral: ())
}

After:

public struct Nil {
  init()
}
public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}

Motivation

Currently, nil differs from other literals: it doesn’t have its own type.
But in some cases we want to deal directly with it, without creating any
instances.

The most important use case is comparison of an Optional to nil.
Currently, this operation is implemented using a hidden struct
_OptionalNilComparisonType,
which is needed precisely because because nil does not have its own type.
Removal of such underscored types is one of the goals stated in Generics
manifesto.

Additionally, declaration of ExpressibleByNilLiteral differs from all other
Expressibles,
because it doesn’t have a Literal type. It is generally beneficial to
eliminate special cases.
Proposed solution

Introduce a struct Nil, which becomes the default type for nil literals:

public struct Nil : ExpressibleByNilLiteral {
  init()
  init(nilLiteral: NilLiteralType)
}
let a = nilprint(type(of: a)) //=> Nil

Rewrite ExpressibleByNilLiteral:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}

Make use of Nil in the standard library:

public func == <T>(left: T?, right: Nil)public func == <T>(left: Nil,
right: T?)public func != <T>(left: T?, right: Nil)public func !=
<T>(left: Nil, right: T?)public func ~= <T>(left: Nil, right: T?)

Source compatibility

Nil identifier is taken, therefore applications that already use it will
stop compiling.
Automatic migration is somewhat possible by renaming of the old entity;
manual migration is recommended.

Applications that use declare ExpressibleByNilLiteral conformances will
stop compiling.
Automatic migration is possible.
Effect on ABI stability

Applications that use Nil identifier will have to make ABI-breaking changes.

Otherwise, the change can mostly be applied in an ABI-compatible manner.


(Chéyo Jiménez) #2

Thank for thinking of this. I am not sure on the advantage of having nil as a concrete type.

Have you seen this talk?

https://realm.io/news/swift-summit-al-skipp-monads/

"The concept of “nil” does not exist in Swift (despite the existence of the keyword nil!)"

Does that talk change your mind about this pitch?

···

On Nov 8, 2016, at 12:30 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Gist link

Introduction

Change nil literal type from () to Nil.
Before:

public protocol ExpressibleByNilLiteral {
  init(nilLiteral: ())
}
After:

public struct Nil {
  init()
}
public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Motivation

Currently, nil differs from other literals: it doesn’t have its own type.
But in some cases we want to deal directly with it, without creating any instances.

The most important use case is comparison of an Optional to nil.
Currently, this operation is implemented using a hidden struct _OptionalNilComparisonType,
which is needed precisely because because nil does not have its own type.
Removal of such underscored types is one of the goals stated in Generics manifesto.

Additionally, declaration of ExpressibleByNilLiteral differs from all other Expressibles,
because it doesn’t have a Literal type. It is generally beneficial to eliminate special cases.

Proposed solution

Introduce a struct Nil, which becomes the default type for nil literals:

public struct Nil : ExpressibleByNilLiteral {
  init()
  init(nilLiteral: NilLiteralType)
}

let a = nil
print(type(of: a)) //=> Nil
Rewrite ExpressibleByNilLiteral:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Make use of Nil in the standard library:

public func == <T>(left: T?, right: Nil)
public func == <T>(left: Nil, right: T?)
public func != <T>(left: T?, right: Nil)
public func != <T>(left: Nil, right: T?)
public func ~= <T>(left: Nil, right: T?)
Source compatibility

Nil identifier is taken, therefore applications that already use it will stop compiling.
Automatic migration is somewhat possible by renaming of the old entity; manual migration is recommended.

Applications that use declare ExpressibleByNilLiteral conformances will stop compiling.
Automatic migration is possible.

Effect on ABI stability

Applications that use Nil identifier will have to make ABI-breaking changes.

Otherwise, the change can mostly be applied in an ABI-compatible manner.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #3

At first glance this doesn’t make any sense to me:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
What’s the need for associatedtype there?

Shouldn’t it be just like this:

public protocol ExpressibleByNilLiteral {

  init(nilLiteral: Nil)
}
I actually would appreciate this change, because of one thing I run into a few days ago.

I’m building a library for BSON (Binary JSON) where I have a struct Document which conforms to ExpressibleByDictionaryLiteral. Than I have a protocol which can convert any type to a value type my document can store.

Now I would have to choose how to implement ExpressibleByDictionaryLiteral:

public init(dictionaryLiteral elements: (String, Element.Value)...)

public init(dictionaryLiteral elements: (String, ElementValueConvertible)…)

If I would go for the second version, I’d lose the ExpressibleByNilLiteral from my value type but I could use every other type naturally.

With the proposed change I could extend Nil with my protocol and make the conversion naturally again.

That is the only advantage I would gain from such a change in the language.

···

--
Adrian Zubarev
Sent with Airmail

Am 8. November 2016 um 21:30:26, Anton Zhilin via swift-evolution (swift-evolution@swift.org) schrieb:

Gist link

Introduction

Change
nil literal type from
() to
Nil.
Before:

public protocol ExpressibleByNilLiteral {
  init(nilLiteral: ())
}
After:

public struct Nil {
  init()
}
public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Motivation

Currently,
nil differs from other literals: it doesn’t have its own type.
But in some cases we want to deal directly with it, without creating any instances.

The most important use case is comparison of an
Optional to
nil.
Currently, this operation is implemented using a hidden struct
_OptionalNilComparisonType,
which is needed precisely because because
nil does not have its own type.
Removal of such underscored types is one of the goals stated in Generics manifesto.

Additionally, declaration of
ExpressibleByNilLiteral differs from all other
Expressibles,
because it doesn’t have a
Literal type. It is generally beneficial to eliminate special cases.

Proposed solution

Introduce a struct
Nil, which becomes the default type for
nil literals:

public struct Nil : ExpressibleByNilLiteral {
  init()
  init(nilLiteral: NilLiteralType)
}

let a = nil
print(type(of: a)) //=> Nil
Rewrite
ExpressibleByNilLiteral:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Make use of
Nil in the standard library:

public func == <T>(left: T?, right: Nil)
public func == <T>(left: Nil, right: T?)
public func != <T>(left: T?, right: Nil)
public func != <T>(left: Nil, right: T?)
public func ~= <T>(left: Nil, right: T?)
Source compatibility

Nil identifier is taken, therefore applications that already use it will stop compiling.
Automatic migration is somewhat possible by renaming of the old entity; manual migration is recommended.

Applications that use declare
ExpressibleByNilLiteral conformances will stop compiling.
Automatic migration is possible.

Effect on ABI stability

Applications that use
Nil identifier will have to make ABI-breaking changes.

Otherwise, the change can mostly be applied in an ABI-compatible manner.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #4

This is a clean approach but that would not solve the example I mentioned.

let document: Document = [
    "key" = nil // won't work for the second version of the implementation I mentioned
]
Do solve this problem here, I’d need a real Nil type I could extend.

extension Nil : MyProtocol { … }
Than the above example will work correctly.

···

--
Adrian Zubarev
Sent with Airmail

Am 8. November 2016 um 21:53:44, Pyry Jahkola (pyry.jahkola@iki.fi) schrieb:

If we start from this thought…

Adrian Zubarev wrote:
Shouldn’t it be just like this:
public protocol ExpressibleByNilLiteral {

  init(nilLiteral: Nil)
}

Then why not just:

public protocol ExpressibleByIntLiteral \{
  static var \`nil\`: Self
\}

…such that Foo.nil creates your nil value of type Foo?

— Pyry


(Pyry Jahkola) #5

If we start from this thought…

Adrian Zubarev wrote:
Shouldn’t it be just like this:
public protocol ExpressibleByNilLiteral {

  init(nilLiteral: Nil)
}

Then why not just:

    public protocol ExpressibleByIntLiteral {
      static var `nil`: Self
    }

…such that Foo.nil creates your nil value of type Foo?

— Pyry


(Anton Zhilin) #6

Thank for thinking of this. I am not sure on the advantage of having nil as

a concrete type.

Have you seen this talk?

https://realm.io/news/swift-summit-al-skipp-monads/

"The concept of “nil” does not exist in Swift (despite the existence of
the keyword nil!)"

Does that talk change your mind about this pitch?

Not much. We can talk about Swift literals being untyped as much as we
want, but still all of them, except for nil, have an internal storage type,
which is also picked by default.
For example, integer literals are Builtin.Int2048, if I’m not mistaken. But
we just can’t store nil without creating an instance of a potentially
complex type.

And this proposal is not about adding nil to all types. You can do this now
with Any, in any case:

let optionalInt1: Any = 42let optionalInt2: Any = () // ewww

···

2016-11-08 23:43 GMT+03:00 Jose Cheyo Jimenez <cheyo@masters3d.com>:


(Anton Zhilin) #7

Then why not just:

    public protocol ExpressibleByIntLiteral {
      static var `nil`: Self
    }

…such that Foo.nil creates your nil value of type Foo?

This proposal is not about creating an alternate syntax for nil as
Optional<Foo>. It’s about granting nil literal its own type:

let a = nilprint(type(of: a)) //=> Nil

···

2016-11-08 23:53 GMT+03:00 Pyry Jahkola <pyry.jahkola@iki.fi>:


(Anton Zhilin) #8

At first glance this doesn’t make any sense to me:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}

What’s the need for associatedtype there?

Shouldn’t it be just like this:

public protocol ExpressibleByNilLiteral {

  init(nilLiteral: Nil)
}

Probably. I just looked at ExpressibleByBooleanLiteral, and it contained

BooleanLiteralType, which should always be Bool by convention.
If someone knows, why this associatedtype is needed in
ExpressibleByBooleanLiteral, please explain.

···

2016-11-08 23:44 GMT+03:00 Adrian Zubarev <adrian.zubarev@devandartist.com>:


(Anton Zhilin) #9

I could not clearly see the exact, concrete problem with the current

implementation. IMAO such fundamental change requires a *very* serious
reason and some thought on performance side of thing.

Performance will not be hurt, because as Robert noted, Nil will be
isomorphic to ().
Concrete problem, as stated in the proposal, is “hacks” that standard
library has to contain, because nil does not have its LiteralType.

···

2016-11-09 0:11 GMT+03:00 arkadi daniyelian <arkdan@icloud.com>:


(Karl) #10

-1

Personally, I don’t like writing “nil” at all. In my understanding of Swift, “nil” is simply a C-like shorthand for "Optional<T>.none”.
If the compiler can’t infer T (such as “let a = nil”), it should fall-back to Optional<Any>.none; I’m very surprised that this isn’t the case currently.

There is a bug where the type-checker can’t always infer the type when you just write “.none”, but outside of that I think it’s a swiftier way of doing things: https://bugs.swift.org/browse/SR-2302

I would be more likely to support the opposite proposal - to eliminate the “nil” literal altogether and replace it with “.none”.

- Karl

···

On 8 Nov 2016, at 21:30, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Gist link <https://gist.github.com/Anton3/ba56a29986c59e9595368be3cb02fb1b>
Introduction

Change nil literal type from () to Nil.
Before:

public protocol ExpressibleByNilLiteral {
  init(nilLiteral: ())
}
After:

public struct Nil {
  init()
}
public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Motivation

Currently, nil differs from other literals: it doesn’t have its own type.
But in some cases we want to deal directly with it, without creating any instances.

The most important use case is comparison of an Optional to nil.
Currently, this operation is implemented using a hidden struct _OptionalNilComparisonType,
which is needed precisely because because nil does not have its own type.
Removal of such underscored types is one of the goals stated in Generics manifesto.

Additionally, declaration of ExpressibleByNilLiteral differs from all other Expressibles,
because it doesn’t have a Literal type. It is generally beneficial to eliminate special cases.

Proposed solution

Introduce a struct Nil, which becomes the default type for nil literals:

public struct Nil : ExpressibleByNilLiteral {
  init()
  init(nilLiteral: NilLiteralType)
}

let a = nil
print(type(of: a)) //=> Nil
Rewrite ExpressibleByNilLiteral:

public protocol ExpressibleByNilLiteral {
  associatedtype NilLiteralType = Nil
  init(nilLiteral: NilLiteralType)
}
Make use of Nil in the standard library:

public func == <T>(left: T?, right: Nil)
public func == <T>(left: Nil, right: T?)
public func != <T>(left: T?, right: Nil)
public func != <T>(left: Nil, right: T?)
public func ~= <T>(left: Nil, right: T?)
Source compatibility

Nil identifier is taken, therefore applications that already use it will stop compiling.
Automatic migration is somewhat possible by renaming of the old entity; manual migration is recommended.

Applications that use declare ExpressibleByNilLiteral conformances will stop compiling.
Automatic migration is possible.

Effect on ABI stability

Applications that use Nil identifier will have to make ABI-breaking changes.

Otherwise, the change can mostly be applied in an ABI-compatible manner.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(John McCall) #11

Is this desirable?

John.

···

On Nov 8, 2016, at 1:07 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

2016-11-08 23:53 GMT+03:00 Pyry Jahkola <pyry.jahkola@iki.fi <mailto:pyry.jahkola@iki.fi>>:

Then why not just:

    public protocol ExpressibleByIntLiteral {
      static var `nil`: Self
    }

…such that Foo.nil creates your nil value of type Foo?

This proposal is not about creating an alternate syntax for nil as Optional<Foo>. It’s about granting nil literal its own type:

let a = nil
print(type(of: a)) //=> Nil


(Chéyo Jiménez) #12

Thank for thinking of this. I am not sure on the advantage of having nil as a concrete type.

Have you seen this talk?

https://realm.io/news/swift-summit-al-skipp-monads/

"The concept of “nil” does not exist in Swift (despite the existence of the keyword nil!)"

Does that talk change your mind about this pitch?

Not much. We can talk about Swift literals being untyped as much as we want, but still all of them, except for nil, have an internal storage type, which is also picked by default.

Having nil be a concrete type would be extremely confusing to me. I believe currently nil gets stripped out during compilation and it gets replaced with their respective Optional<Type>.none
nil is just a convenient way to work with optionals. The same goes to implicit unwrap optionals !

I guess I don't see a reason why nil should be a concrete at all.

···

On Nov 8, 2016, at 1:05 PM, Anton Zhilin <antonyzhilin@gmail.com> wrote:
2016-11-08 23:43 GMT+03:00 Jose Cheyo Jimenez <cheyo@masters3d.com>:

For example, integer literals are Builtin.Int2048, if I’m not mistaken. But we just can’t store nil without creating an instance of a potentially complex type.

And this proposal is not about adding nil to all types. You can do this now with Any, in any case:

let optionalInt1: Any = 42
let optionalInt2: Any = () // ewww


(Robert Widmann) #13

-1. This type is isomorphic to () from a language-internal perspective and will introduce an extra name for which no useful algorithms can be written against from an external one - which makes the proposal isomorphic to one that introduces a keyword without significant justification.

~Robert Widmann

2016/11/08 16:07、Anton Zhilin via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

2016-11-08 23:53 GMT+03:00 Pyry Jahkola <pyry.jahkola@iki.fi>:

Then why not just:

    public protocol ExpressibleByIntLiteral {
      static var `nil`: Self
    }

…such that Foo.nil creates your nil value of type Foo?

This proposal is not about creating an alternate syntax for nil as Optional<Foo>. It’s about granting nil literal its own type:

let a = nil
print(type(of: a)) //=> Nil
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #14

Could you elaborate an implementation for one of these functions:

func == <T>(left: T?, right: Nil)

I’m a little confused how these would work, because the types are different and the optional is either .none or .some(T) of type Optional<T> where the other side is Nil.

···

--
Adrian Zubarev
Sent with Airmail

Am 8. November 2016 um 22:07:53, Anton Zhilin (antonyzhilin@gmail.com) schrieb:

2016-11-08 23:53 GMT+03:00 Pyry Jahkola <pyry.jahkola@iki.fi>:

Then why not just:

public protocol ExpressibleByIntLiteral \{
  static var \`nil\`: Self
\}

…such that Foo.nil creates your nil value of type Foo?
This proposal is not about creating an alternate syntax for
nil as Optional<Foo>. It’s about granting
nil literal its own type:

let a = nil
print(type(of: a)) //=> Nil


(Anton Zhilin) #15

Could you elaborate an implementation for one of these functions:

func == <T>(left: T?, right: Nil) {
    switch left {
        case .some(_): return false
        case .none: return true
    }
}

The implementation is basically the same as of now, except that instead of
Nil, there is _OptionalNilComparisonType.

Main use case of Nil would essentially be partial specialization of
functions.
See example with optional comparison: there exists a generic version that
takes a proper ExpressibleByNilLiteral (in this case Optional<T>), but we
can apply simpler logic in case we see nil.

Frankly speaking, I’m not sure myself this feature would find any use
except for the mentioned optional comparison operators.
So I will probably stop pushing this forward.

···

2016-11-09 0:12 GMT+03:00 Adrian Zubarev <adrian.zubarev@devandartist.com>:


(Karl) #16

Oh, it is the case currently:

let a = nil
error: repl.swift:1:9: error: 'nil' requires a contextual type

I’m not familiar with why the compiler needs _OptionalNilComparisonType in order to compare to “nil”, but I would guess it could be improved to recognise the literal natively if removing this protocol is a significant concern.

- Karl

···

On 9 Nov 2016, at 16:55, Karl <raziel.im+swift-evo@gmail.com> wrote:

If the compiler can’t infer T (such as “let a = nil”), it should fall-back to Optional<Any>.none; I’m very surprised that this isn’t the case currently.