[Pitch] Add the DefaultConstructible protocol to the standard library

Array already has init(repeating:count:) that puts the responsibility of

choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists,

Because encapsulation. There's a reason NSObject has a default initializer.

···

On Mon, Dec 26, 2016 at 12:10 PM, Tony Allevato via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of
choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists, and it doesn't impose additional requirements on the element type.
For example, the user could get the default from a static factory method,
an instance method on another object, or something else entirely.

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

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

Yeah, that's what I would use… The "filled out" example would be:
extension Array {
    public func pmap<T: DefaultInitable> (transform: (Element) -> T) -> [T] {
        var result = Array<T>(repeating: T(), count: self.count) //Pick a T... any T...
        for i in self.indices {
            result[i] = whateverTheConcurrentExectutionSyntaxIs(self[i], transform)
        }
        return result
    }
}
var thisCouldTakeAWhile = Array((0...10000)).pmap {
    someReallySlowFunction($0)
}
At least I think that’d work... I haven’t tried it yet... Anyway, without some way (any way) of getting an instance of T to fill in the `result` array, it becomes much trickier to keep track of all the concurrently-calculated transformed values. In this case, the semantics of `T()` are fairly irrelevant because the semantics of the overall statement is just to work around a language limitation (Swift not having separate allocation and initialization phases), which doesn’t have anything to do with the semantics of the initial value that got passed as the `repeating` argument.

- Dave Sweeris

···

On Dec 26, 2016, at 12:10, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com <mailto:allevato@google.com>> wrote:

Mathematically, identities are associated with (type, operation) pairs, not types alone.

This conversation has put me in the column of "numeric types shouldn't have default initializers at all", personally.

I'd agree, except sometimes you need a T, any T, for when you want to create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because it'll still crash if you try to store anything in a[i] without somehow putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of choosing the default value at the call site. If someone were writing a generic algorithm around this, then why not just propagate that responsibility out to its call site as well? That way, the algorithm isn't making any assumptions about what the "default" value is or even if one exists, and it doesn't impose additional requirements on the element type. For example, the user could get the default from a static factory method, an instance method on another object, or something else entirely.

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

Please don't do this. Dave has already confirmed that `init()` makes no
semantic guarantees as to the value returned. It _happens_ to be the case
that Int() == 0. That does _not_ mean that semantically `Int.init()`
provides the identity element for Int.

···

On Mon, Dec 26, 2016 at 2:27 PM, Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < >> swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"
https://lists.swift.org/pipermail/swift-evolution/Week-of-
Mon-20160704/023642.html

Imho "DefaultConstructible" types can be very handy, but so far, it seems
no one has presented a single use case that is important enough to justify
the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for
for me, the question is "Does this protocol confuse users of Swift?", which
I'd answer with "yes, possibly" (unless someone comes up with a name that
is more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

If you have _no_ guarantees as to what T is besides that it can be
instantiated with T(), what's the point of having a [T] with not even a
single actual T? Surely, there is nothing you can do with a [T] with a
single placeholder T of which you have no knowledge other than it is a T?
Why would you not reserve the capacity for a [T] at the point when you have
at least one actual T?

- Dave Sweeris

···

On Mon, Dec 26, 2016 at 2:57 PM, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

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

> The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

It represents a sensible value to initialize an int to when I want to
initialize an array of ints to a certain size. There is a reason why you
zero out memory but you don't "one out" memory.

I have definitely "oned out" memory before.

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

Because the identity associated with the default initializer as is is the
additive identity which means the default operation is addition.

Again, please don't do this. Dave has already explained that `init()` makes
not semantic guarantees about identity. You are reasoning backwards, and at
this point it seems you're doing this deliberately and against all reasoned
explanations as to why you're mistaken.

···

On Mon, Dec 26, 2016 at 3:14 PM, Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 11:57 AM, Tony Allevato <tony.allevato@gmail.com> > wrote:

On Mon, Dec 26, 2016 at 11:43 AM Adam Nemecek via swift-evolution < >> swift-evolution@swift.org> wrote:

> For integers, 0 is an additive identity. Is there a reason it should
be given special treatment over 1, the multiplicative identity?

E.g. for statistical reasons. When I have a collection of users with age
etc it makes sense to ask what is the combined age of the collection? What
is the semantic meaning of multiplying their ages?

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

I would turn your example around—if you're interested enough in thorough
type design that you feel that a DefaultConstructible protocol would be
useful here, then I offer that a better and safer design would be to create
an "Age" value type (or, more generally, measurement types with concepts of
units) if you want compile-time safety and limiting the supported
operations to only those that are sensical. "Int" is arguably too wide a
type to represent an age in a public API because it would allow two ages to
be multiplied together, as you said.

> Mathematically, identities are associated with (type, operation)
pairs, not types alone.

Correct, however we aren't talking about mathematics, we are talking
about the implementation of a language that runs on very concrete
architectures where very concrete bit patterns mean very concrete things
that are unlikely to change any time soon.

The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

On Mon, Dec 26, 2016 at 11:35 AM, Tony Allevato <allevato@google.com> >>> wrote:

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.
On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < >>> swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> >>> wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < >>> swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mo
n-20160704/023642.html

Imho "DefaultConstructible" types can be very handy, but so far, it
seems no one has presented a single use case that is important enough to
justify the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for
for me, the question is "Does this protocol confuse users of Swift?", which
I'd answer with "yes, possibly" (unless someone comes up with a name that
is more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

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

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

> Array already has init(repeating:count:) that puts the responsibility
of choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists,

Because encapsulation. There's a reason NSObject has a default initializer.

Can you elaborate? What is the reason that NSObject has a default
initializer? I suspect it has to do with Objective-C "stuff," but can you
point me to a resource explaining the rationale behind the design of
NSObject?

···

On Mon, Dec 26, 2016 at 3:17 PM, Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 12:10 PM, Tony Allevato via swift-evolution < > swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution < >> swift-evolution@swift.org> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of
choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists, and it doesn't impose additional requirements on the element type.
For example, the user could get the default from a static factory method,
an instance method on another object, or something else entirely.

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

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

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

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs, not
types alone.

This conversation has put me in the column of "numeric types shouldn't have
default initializers at all", personally.

···

On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> > wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < > swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"

[swift-evolution] [Pitch] Removing the empty initialiser requirement from RangeReplaceableCollection

Imho "DefaultConstructible" types can be very handy, but so far, it seems
no one has presented a single use case that is important enough to justify
the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for for
me, the question is "Does this protocol confuse users of Swift?", which I'd
answer with "yes, possibly" (unless someone comes up with a name that is
more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

1 Like

> For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity?

E.g. for statistical reasons. When I have a collection of users with age
etc it makes sense to ask what is the combined age of the collection? What
is the semantic meaning of multiplying their ages?

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

I would turn your example around—if you're interested enough in thorough
type design that you feel that a DefaultConstructible protocol would be
useful here, then I offer that a better and safer design would be to create
an "Age" value type (or, more generally, measurement types with concepts of
units) if you want compile-time safety and limiting the supported
operations to only those that are sensical. "Int" is arguably too wide a
type to represent an age in a public API because it would allow two ages to
be multiplied together, as you said.

> Mathematically, identities are associated with (type, operation) pairs,
not types alone.

Correct, however we aren't talking about mathematics, we are talking about
the implementation of a language that runs on very concrete architectures
where very concrete bit patterns mean very concrete things that are
unlikely to change any time soon.

The all-zero bit pattern represents the integer zero—that's not the same as
whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

···

On Mon, Dec 26, 2016 at 11:43 AM Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 11:35 AM, Tony Allevato <allevato@google.com> > wrote:

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.
On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < > swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> > wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < > swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"

[swift-evolution] [Pitch] Removing the empty initialiser requirement from RangeReplaceableCollection

Imho "DefaultConstructible" types can be very handy, but so far, it seems
no one has presented a single use case that is important enough to justify
the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for for
me, the question is "Does this protocol confuse users of Swift?", which I'd
answer with "yes, possibly" (unless someone comes up with a name that is
more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

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

1 Like

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of
choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists, and it doesn't impose additional requirements on the element type.
For example, the user could get the default from a static factory method,
an instance method on another object, or something else entirely.

···

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

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

1 Like

> The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

It represents a sensible value to initialize an int to when I want to
initialize an array of ints to a certain size. There is a reason why you
zero out memory but you don't "one out" memory.

It was previously mentioned in this thread, but Swift explicitly made the
choice to *not* initialize values to zero by default. If you want to
initialize an array of ints, you can just as easily pass the default in
that you want. If you want to do this more generically, you can still push
the responsibility to the caller, which is arguably better because it
doesn't restrict your algorithm to only those data types that conform to a
particular protocol.

The fact that certain bit-patterns correspond to zero and that zero has
been a default in other languages is an artifact of their hardware
representation, but not necessarily one that should motivate higher level
design.

> That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

Because the identity associated with the default initializer as is is the
additive identity which means the default operation is addition.

I should have phrased more carefully: it doesn't explain why the additive
identity *should be* more special.

There is no "default operation" over integers. The fact that the additive
identity also happens to be the all zero bit-pattern which also happens to
be the default initialized state isn't an argument for why it *should* be
that way—it's just explaining what the current state of things is. It's a
circular argument to use that as a reason for why default constructibility
should exist as a consequence of that.

Because encapsulation. There's a reason NSObject has a default

initializer.

That's not an encapsulation that works everywhere. Consider this: there
exist types that might have reasonable "default" values but for which the
default initializer isn't an appropriate or possible way to express it.
(One example: an ORM where objects have to be associated with an
originating "context"; either the type needs to have an initializer that
takes a context, or the context is a factory that returns instances.) By
writing an algorithm that relies on DefaultConstructible, you prohibit
those types from being used there. If the algorithm pushes the
responsibility to the caller, as Array(repeating:count:) does, then it can
work for both types. It's *more* general and more powerful than the
DefaultConstructible version.

The reason NSObject has a default initializer is because every object
inherits from it and it must have a sensible default initializer to end the
call chain, and there's nothing meaningful that needs to be parameterized
for NSObject. If you call [[NSObject alloc] init] by itself, you get back
an object that doesn't really do much. But I'm not sure what that has to do
with generic algorithms—what about all the types that extend NSObject that
don't have default initializers?

···

On Mon, Dec 26, 2016 at 12:15 PM Adam Nemecek <adamnemecek@gmail.com> wrote:

On Mon, Dec 26, 2016 at 11:57 AM, Tony Allevato <tony.allevato@gmail.com> > wrote:

On Mon, Dec 26, 2016 at 11:43 AM Adam Nemecek via swift-evolution < > swift-evolution@swift.org> wrote:

> For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity?

E.g. for statistical reasons. When I have a collection of users with age
etc it makes sense to ask what is the combined age of the collection? What
is the semantic meaning of multiplying their ages?

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

I would turn your example around—if you're interested enough in thorough
type design that you feel that a DefaultConstructible protocol would be
useful here, then I offer that a better and safer design would be to create
an "Age" value type (or, more generally, measurement types with concepts of
units) if you want compile-time safety and limiting the supported
operations to only those that are sensical. "Int" is arguably too wide a
type to represent an age in a public API because it would allow two ages to
be multiplied together, as you said.

> Mathematically, identities are associated with (type, operation) pairs,
not types alone.

Correct, however we aren't talking about mathematics, we are talking about
the implementation of a language that runs on very concrete architectures
where very concrete bit patterns mean very concrete things that are
unlikely to change any time soon.

The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

On Mon, Dec 26, 2016 at 11:35 AM, Tony Allevato <allevato@google.com> > wrote:

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.
On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < > swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> > wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < > swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"

[swift-evolution] [Pitch] Removing the empty initialiser requirement from RangeReplaceableCollection

Imho "DefaultConstructible" types can be very handy, but so far, it seems
no one has presented a single use case that is important enough to justify
the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for for
me, the question is "Does this protocol confuse users of Swift?", which I'd
answer with "yes, possibly" (unless someone comes up with a name that is
more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

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

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of
choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists, and it doesn't impose additional requirements on the element type.
For example, the user could get the default from a static factory method,
an instance method on another object, or something else entirely.

Yeah, that's what I would use… The "filled out" example would be:

extension Array {
    public func pmap<T: DefaultInitable> (transform: (Element) -> T) -> [T]
{
        var result = Array<T>(repeating: T(), count: self.count) //Pick a
T... any T...
        for i in self.indices {
            result[i] = whateverTheConcurrentExectutionSyntaxIs(self[i],
transform)
        }
        return result
    }
}
var thisCouldTakeAWhile = Array((0...10000)).pmap {
    someReallySlowFunction($0)
}

At least I *think* that’d work... I haven’t tried it yet... Anyway,
without some way (*any* way) of getting an instance of T to fill in the
`result` array, it becomes much trickier to keep track of all the
concurrently-calculated transformed values. In this case, the semantics of
`T()` are fairly irrelevant because the semantics of the *overall
statement* is just to work around a language limitation (Swift not having
separate allocation and initialization phases), which doesn’t have anything
to do with the semantics of the initial value that got passed as the
`repeating` argument.

This looks like it's abusing T() to stand in for nil, though. Before the
result[i] assignment executes, result[i] shouldn't conceptually have a
value—you're putting a default in there because the implementation requires
it. T() as a placeholder is just as valid/invalid as any other value in T's
space.

It's a square-peg-in-a-round-hole problem—design-wise, an array of
optionals would be a better fit, but you rightly don't want to return that
from the function, and you don't want to bear the cost of converting the
array of optionals to an array of non-optionals after the computation is
complete. That makes complete sense, but perhaps *that* is the problem that
should be addressed, instead of trying to use T() to avoid it? Or
alternatively, a parallel map operation should return an array of futures
(or a collection type that itself is a future).

···

On Mon, Dec 26, 2016 at 1:19 PM David Sweeris <davesweeris@mac.com> wrote:

On Dec 26, 2016, at 12:10, Tony Allevato <tony.allevato@gmail.com> wrote:
On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:
On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

- Dave Sweeris

Yes, those particular types have initializers that take no arguments.

That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

Right, it's an empirical argument.

I didn't think the value returned by `init()` was regarded as any sort of

zero--or even any sort of "default." In fact, some types in Foundation have
a static property called `default` distinct from `init()`.

Let's not talk about those then. This would not apply to every single type
in existence, as I've stated previously.

It gives you something different every time. How can this be squared with

your stated motivation regarding concepts of zero and concepts of equality?

Due to the fact that it's a resource, not a value. As I've stated above,
not all of this applies to types that are more resource-like.

Or, it's what you get because that's the most trivial possible string.

Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

This is an appeal to tradition.

The statement I wrote was in JavaScript, so I'm not sure what you mean by

returning an optional. `.reduce((a, b) => a + b)` results in an error in
JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

I was talking about their analogous swift implementations.

Can you give an example of an algorithm dealing with tensors where you

would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols?

If it's implemented as either nested collections or numbers.

···

On Sun, Dec 25, 2016 at 6:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 7:30 PM, Adam Nemecek <adamnemecek@gmail.com> > wrote:

> Is it well settled, either in Swift or in C++/Rust/etc., that the
value returned by a default initializer/constructor is regarded as an
identity element or zero?

Int() == 0, String() == "" so to some extent by convention, a lot of
types have a default value as is.

Yes, those particular types have initializers that take no arguments. That
does not address my question. You merely restated your initial observation
that many types in Swift have implemented `init()`.

I didn't think the value returned by `init()` was regarded as any sort of
zero--or even any sort of "default." In fact, some types in Foundation have
a static property called `default` distinct from `init()`. In Rust, the
Default trait requires a function called `default()`, which is documented
as being useful when you want "some kind of default value, and don't
particularly care what it is."

I was asking whether there's some understanding, of which I've been
unaware, that the result of `init()` (or the equivalent in other languages)
is expected to be some sort of zero or an identity element. I'm not aware
of any evidence to that effect. Are you?

> Is the thread that I get by writing `let t = Thread()` some kind of zero

in any reasonable sense of the word?

DefaultConstructibility makes less sense for types that represent some
sort of resource but make sense for things that are values. But even in
this case, Thread() gives you a default value for example if you are
working with a resizable collection of threads.

It gives you something different every time. How can this be squared with
your stated motivation regarding concepts of zero and concepts of equality?

A better question is why does thread currently implement a default

constructor?

It's an initializer that takes no arguments, because none are needed for a
new thread. How else would you write it?

> Do you mean to argue that for an integer the additive identity should be

considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

I do. The justification is that if I call the default constructor of Int
currently, I get the value of 0.

This is backwards. Why do you believe that the value you obtain from
`init()` is intended to be an identity element at all, let alone the most
important one? (It's also circular reasoning. Since `init()` only ever
gives you one value at a time, by your reasoning it demonstrates that every
type must have one "more prominent and useful" identity, which is begging
the question.)

Which means that the binary operation must be addition.

Based on the value of `Int.init()`, you conclude that addition of integers
is a "more prominent and useful" operation than multiplication? Again, this
is backwards. Rather, we know that each numerical type belongs to multiple
ring algebras; there is no basis for reckoning any as "more useful." Since
`init()` can only ever give us one value at a time, we know that `init()`
cannot give a value that is a meaningful default with respect to any
particular operation.

If I call String() I get "" which is the identity of the + String

operation.

Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

> Going to your original example, I should add: other languages provide a

version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

These two will have different signatures. The reduce you describe returns
an optional,

The statement I wrote was in JavaScript, so I'm not sure what you mean by
returning an optional. `.reduce((a, b) => a + b)` results in an error
in JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

the other one would returns the default value.

In what scenario would you prefer to propagate a default after reducing a
potential empty collection _without supplying an explicit default_ for that
operation? This would certainly violate the Swift convention of not
defaulting to zero and, I suspect, most users of Swift would not regard
that as ergonomic at all.

Fundamentally the default constructibles are useful in numerical
computations e..g. dealing with tensors.

Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols? (I should also add, FWIW, I
have never seen a generic algorithm written for integers or FP types that
has preferred the use of `T()` over `0`.)

On Sun, Dec 25, 2016 at 3:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 5:27 PM, Adam Nemecek <adamnemecek@gmail.com> >>> wrote:

> *Which* APIs become more ergonomic?

I'll get back to this question in a second if I may. This would be a
longer discussion and I first want to make sure that before we get into the
details that there is a possibility of this being introduced (I'm asking if
violating the no zero defaults is more important than slightly more
ergonomic APIs). But to give a broad answer I think that the concept of a
zero is closely related to the concept of equality (and all the things that
build up on equality such as comparability and negation).

> 1) How does this square with Swift’s general philosophy to not
default initialize values to “zero”?

I actually wasn't aware of this philosophy. Despite this philosophy,
look at how many types actually currently implement a default constructor.

(Not a rhetorical question:) Is it well settled, either in Swift or in
C++/Rust/etc., that the value returned by a default initializer/constructor
is regarded as an identity element or zero? Is the thread that I get by
writing `let t = Thread()` some kind of zero in any reasonable sense of the
word?

Also can I ask what's the motivation behind this philosophy?
I think that in Swift, default constructibility makes complete sense
for (most?) structs, maybe less so for classes.

> 2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

This is a good point that I've considered as well but felt that for the
most part, there is one particular identity and associated operation that
is more prominent and useful than others. Furthermore, modeling different
algebras isn't mutually exclusive with writing generic algorithms that rely
on this protocol, you can always introduce some monoidic wrapper type that
defines the more appropriate default value and operation.

Do you mean to argue that for an integer the additive identity should be
considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

Going to your original example, I should add: other languages provide a
version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

On Sun, Dec 25, 2016 at 1:24 PM, Chris Lattner <clattner@apple.com> >>>> wrote:

On Dec 25, 2016, at 12:54 PM, Adam Nemecek via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

Does enabling a lot of small improvements that make APIs more
ergonomic count as practical?

Yes, that would count as practical, but Xiaodi’s question is just as
important. *Which* APIs become more ergonomic?

Here are a couple of more questions:

1) How does this square with Swift’s general philosophy to not default
initialize values to “zero”?

2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

-Chris

On Sun, Dec 25, 2016 at 12:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>> wrote:

On Sun, Dec 25, 2016 at 3:07 PM, Adam Nemecek <adamnemecek@gmail.com> >>>>>> wrote:

There's a book that provides quite a bit of info on this

https://smile.amazon.com/Elements-Programming-Alexander-Step
anov/dp/032163537X?sa-no-redirect=1

They say that DefaultConstructible is one of the essential protocols
on which most algorithms rely in one way or another. One of the authors is
the designer of the C++ STL and basically the father of modern generics.

This protocol is important for any algebraic structure that deals
with the concept of appending or addition (as "zero" is one of the
requirements of monoid). There isn't a good short answer to your question.
It's a building block of algorithms. Think about why a
RangeReplaceableCollection can provide you with a default constructor but a
Collection can't.

It's well and fine that most algorithms rely on the concept in one
way or another. Yet the Swift standard library already implements many
generic algorithms but has no DefaultConstructible, presumably because
there are other protocols that guarantee `init()` and the algorithms being
implemented don't need to be (practically speaking) generic over all
DefaultConstructible types. My question is: what practical use cases are
there for an explicit DefaultConstructible that are impractical today?

On Sun, Dec 25, 2016 at 11:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>>>> wrote:

Can you give some other examples of generic algorithms that would
make use of this DefaultConstructible? I'm having trouble coming up with
any other than reduce.
On Sun, Dec 25, 2016 at 14:23 Adam Nemecek via swift-evolution < >>>>>>>> swift-evolution@swift.org> wrote:

This protocol is present in C++ http://en.cppreference.com
/w/cpp/concept/DefaultConstructible as well as in Rust
std::default - Rust

It's the identity element/unit of a monoid or a zero.

The Swift implementation is very simple (I'm open to different
names)

protocol DefaultConstructible {
    init()
}

A lot of the standard types could then be made to conform to this
protocol. These include all the numeric types, collection types (array,
set, dict), string, basically at least every type that currently has a
constructor without any arguments.

The RangeReplaceableCollection protocol would inherit from this
protocol as well.

This protocol would simplify a lot of generic algorithms where you
need the concept of a zero (which shows up a lot)

Once introduced, Sequence could define an alternative
implementation of reduce where the initial result doesn't need to be
provided as it can be default constructed.

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

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

> Yes, those particular types have initializers that take no arguments.
That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

Right, it's an empirical argument.

My question was about the semantics of `init()`; it can't be answered
empirically and requires no argument.

> I didn't think the value returned by `init()` was regarded as any sort
of zero--or even any sort of "default." In fact, some types in Foundation
have a static property called `default` distinct from `init()`.

Let's not talk about those then. This would not apply to every single type
in existence, as I've stated previously.

> It gives you something different every time. How can this be squared
with your stated motivation regarding concepts of zero and concepts of
equality?

Due to the fact that it's a resource, not a value. As I've stated above,
not all of this applies to types that are more resource-like.

> Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

This is an appeal to tradition.

I'm saying that `init()` does not, afaik, currently have the semantics that
you state it does. I am not making an argument.

The statement I wrote was in JavaScript, so I'm not sure what you mean by
returning an optional. `.reduce((a, b) => a + b)` results in an error
in JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

I was talking about their analogous swift implementations.

Can you elaborate, perhaps in code, as to what you would improve about
reduce?

> Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols?

If it's implemented as either nested collections or numbers.

I'm not sure I follow. Can you give an example of such an algorithm?

···

On Sun, Dec 25, 2016 at 9:40 PM, Adam Nemecek <adamnemecek@gmail.com> wrote:

On Sun, Dec 25, 2016 at 6:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 7:30 PM, Adam Nemecek <adamnemecek@gmail.com> >> wrote:

> Is it well settled, either in Swift or in C++/Rust/etc., that the
value returned by a default initializer/constructor is regarded as an
identity element or zero?

Int() == 0, String() == "" so to some extent by convention, a lot of
types have a default value as is.

Yes, those particular types have initializers that take no arguments.
That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

I didn't think the value returned by `init()` was regarded as any sort of
zero--or even any sort of "default." In fact, some types in Foundation have
a static property called `default` distinct from `init()`. In Rust, the
Default trait requires a function called `default()`, which is documented
as being useful when you want "some kind of default value, and don't
particularly care what it is."

I was asking whether there's some understanding, of which I've been
unaware, that the result of `init()` (or the equivalent in other languages)
is expected to be some sort of zero or an identity element. I'm not aware
of any evidence to that effect. Are you?

> Is the thread that I get by writing `let t = Thread()` some kind of

zero in any reasonable sense of the word?

DefaultConstructibility makes less sense for types that represent some
sort of resource but make sense for things that are values. But even in
this case, Thread() gives you a default value for example if you are
working with a resizable collection of threads.

It gives you something different every time. How can this be squared with
your stated motivation regarding concepts of zero and concepts of equality?

A better question is why does thread currently implement a default

constructor?

It's an initializer that takes no arguments, because none are needed for
a new thread. How else would you write it?

> Do you mean to argue that for an integer the additive identity should

be considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

I do. The justification is that if I call the default constructor of Int
currently, I get the value of 0.

This is backwards. Why do you believe that the value you obtain from
`init()` is intended to be an identity element at all, let alone the most
important one? (It's also circular reasoning. Since `init()` only ever
gives you one value at a time, by your reasoning it demonstrates that every
type must have one "more prominent and useful" identity, which is begging
the question.)

Which means that the binary operation must be addition.

Based on the value of `Int.init()`, you conclude that addition of
integers is a "more prominent and useful" operation than multiplication?
Again, this is backwards. Rather, we know that each numerical type belongs
to multiple ring algebras; there is no basis for reckoning any as "more
useful." Since `init()` can only ever give us one value at a time, we know
that `init()` cannot give a value that is a meaningful default with respect
to any particular operation.

If I call String() I get "" which is the identity of the + String

operation.

Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

> Going to your original example, I should add: other languages provide

a version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

These two will have different signatures. The reduce you describe
returns an optional,

The statement I wrote was in JavaScript, so I'm not sure what you mean by
returning an optional. `.reduce((a, b) => a + b)` results in an error
in JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

the other one would returns the default value.

In what scenario would you prefer to propagate a default after reducing a
potential empty collection _without supplying an explicit default_ for that
operation? This would certainly violate the Swift convention of not
defaulting to zero and, I suspect, most users of Swift would not regard
that as ergonomic at all.

Fundamentally the default constructibles are useful in numerical
computations e..g. dealing with tensors.

Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols? (I should also add, FWIW, I
have never seen a generic algorithm written for integers or FP types that
has preferred the use of `T()` over `0`.)

On Sun, Dec 25, 2016 at 3:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 5:27 PM, Adam Nemecek <adamnemecek@gmail.com> >>>> wrote:

> *Which* APIs become more ergonomic?

I'll get back to this question in a second if I may. This would be a
longer discussion and I first want to make sure that before we get into the
details that there is a possibility of this being introduced (I'm asking if
violating the no zero defaults is more important than slightly more
ergonomic APIs). But to give a broad answer I think that the concept of a
zero is closely related to the concept of equality (and all the things that
build up on equality such as comparability and negation).

> 1) How does this square with Swift’s general philosophy to not
default initialize values to “zero”?

I actually wasn't aware of this philosophy. Despite this philosophy,
look at how many types actually currently implement a default constructor.

(Not a rhetorical question:) Is it well settled, either in Swift or in
C++/Rust/etc., that the value returned by a default initializer/constructor
is regarded as an identity element or zero? Is the thread that I get by
writing `let t = Thread()` some kind of zero in any reasonable sense of the
word?

Also can I ask what's the motivation behind this philosophy?
I think that in Swift, default constructibility makes complete sense
for (most?) structs, maybe less so for classes.

> 2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

This is a good point that I've considered as well but felt that for
the most part, there is one particular identity and associated operation
that is more prominent and useful than others. Furthermore, modeling
different algebras isn't mutually exclusive with writing generic algorithms
that rely on this protocol, you can always introduce some monoidic wrapper
type that defines the more appropriate default value and operation.

Do you mean to argue that for an integer the additive identity should
be considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

Going to your original example, I should add: other languages provide a
version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

On Sun, Dec 25, 2016 at 1:24 PM, Chris Lattner <clattner@apple.com> >>>>> wrote:

On Dec 25, 2016, at 12:54 PM, Adam Nemecek via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

Does enabling a lot of small improvements that make APIs more
ergonomic count as practical?

Yes, that would count as practical, but Xiaodi’s question is just as
important. *Which* APIs become more ergonomic?

Here are a couple of more questions:

1) How does this square with Swift’s general philosophy to not
default initialize values to “zero”?

2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

-Chris

On Sun, Dec 25, 2016 at 12:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>>> wrote:

On Sun, Dec 25, 2016 at 3:07 PM, Adam Nemecek <adamnemecek@gmail.com >>>>>>> > wrote:

There's a book that provides quite a bit of info on this

https://smile.amazon.com/Elements-Programming-Alexander-Step
anov/dp/032163537X?sa-no-redirect=1

They say that DefaultConstructible is one of the essential
protocols on which most algorithms rely in one way or another. One of the
authors is the designer of the C++ STL and basically the father of modern
generics.

This protocol is important for any algebraic structure that deals
with the concept of appending or addition (as "zero" is one of the
requirements of monoid). There isn't a good short answer to your question.
It's a building block of algorithms. Think about why a
RangeReplaceableCollection can provide you with a default constructor but a
Collection can't.

It's well and fine that most algorithms rely on the concept in one
way or another. Yet the Swift standard library already implements many
generic algorithms but has no DefaultConstructible, presumably because
there are other protocols that guarantee `init()` and the algorithms being
implemented don't need to be (practically speaking) generic over all
DefaultConstructible types. My question is: what practical use cases are
there for an explicit DefaultConstructible that are impractical today?

On Sun, Dec 25, 2016 at 11:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>>>>> wrote:

Can you give some other examples of generic algorithms that would
make use of this DefaultConstructible? I'm having trouble coming up with
any other than reduce.
On Sun, Dec 25, 2016 at 14:23 Adam Nemecek via swift-evolution < >>>>>>>>> swift-evolution@swift.org> wrote:

This protocol is present in C++ http://en.cppreference.com
/w/cpp/concept/DefaultConstructible as well as in Rust
std::default - Rust

It's the identity element/unit of a monoid or a zero.

The Swift implementation is very simple (I'm open to different
names)

protocol DefaultConstructible {
    init()
}

A lot of the standard types could then be made to conform to this
protocol. These include all the numeric types, collection types (array,
set, dict), string, basically at least every type that currently has a
constructor without any arguments.

The RangeReplaceableCollection protocol would inherit from this
protocol as well.

This protocol would simplify a lot of generic algorithms where
you need the concept of a zero (which shows up a lot)

Once introduced, Sequence could define an alternative
implementation of reduce where the initial result doesn't need to be
provided as it can be default constructed.

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

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

> Yes, those particular types have initializers that take no arguments.
That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

Right, it's an empirical argument.

> I didn't think the value returned by `init()` was regarded as any sort
of zero--or even any sort of "default." In fact, some types in Foundation
have a static property called `default` distinct from `init()`.

Let's not talk about those then. This would not apply to every single type
in existence, as I've stated previously.

Whoops, I missed a few items here. In your first post, you stated that you
wanted your proposed protocol to apply to "basically at least every type
that currently has a constructor without any arguments." Is that not the
case?

It gives you something different every time. How can this be squared with
your stated motivation regarding concepts of zero and concepts of equality?

Due to the fact that it's a resource, not a value. As I've stated above,
not all of this applies to types that are more resource-like.

In Swift, protocols do not merely guarantee particular spellings, but
particular semantics as well. If "not all of this applies" to
"resource-like" types, what semantic guarantees are you proposing for
`DefaultConstructible`, and to what types would they completely apply?

···

On Sun, Dec 25, 2016 at 9:40 PM, Adam Nemecek <adamnemecek@gmail.com> wrote:

Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

This is an appeal to tradition.

> The statement I wrote was in JavaScript, so I'm not sure what you mean
by returning an optional. `.reduce((a, b) => a + b)` results in an
error in JavaScript. In Swift, such a function may also be implemented with
a precondition that the array is not empty and would not return an optional.

I was talking about their analogous swift implementations.

> Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols?

If it's implemented as either nested collections or numbers.

On Sun, Dec 25, 2016 at 6:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 7:30 PM, Adam Nemecek <adamnemecek@gmail.com> >> wrote:

> Is it well settled, either in Swift or in C++/Rust/etc., that the
value returned by a default initializer/constructor is regarded as an
identity element or zero?

Int() == 0, String() == "" so to some extent by convention, a lot of
types have a default value as is.

Yes, those particular types have initializers that take no arguments.
That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

I didn't think the value returned by `init()` was regarded as any sort of
zero--or even any sort of "default." In fact, some types in Foundation have
a static property called `default` distinct from `init()`. In Rust, the
Default trait requires a function called `default()`, which is documented
as being useful when you want "some kind of default value, and don't
particularly care what it is."

I was asking whether there's some understanding, of which I've been
unaware, that the result of `init()` (or the equivalent in other languages)
is expected to be some sort of zero or an identity element. I'm not aware
of any evidence to that effect. Are you?

> Is the thread that I get by writing `let t = Thread()` some kind of

zero in any reasonable sense of the word?

DefaultConstructibility makes less sense for types that represent some
sort of resource but make sense for things that are values. But even in
this case, Thread() gives you a default value for example if you are
working with a resizable collection of threads.

It gives you something different every time. How can this be squared with
your stated motivation regarding concepts of zero and concepts of equality?

A better question is why does thread currently implement a default

constructor?

It's an initializer that takes no arguments, because none are needed for
a new thread. How else would you write it?

> Do you mean to argue that for an integer the additive identity should

be considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

I do. The justification is that if I call the default constructor of Int
currently, I get the value of 0.

This is backwards. Why do you believe that the value you obtain from
`init()` is intended to be an identity element at all, let alone the most
important one? (It's also circular reasoning. Since `init()` only ever
gives you one value at a time, by your reasoning it demonstrates that every
type must have one "more prominent and useful" identity, which is begging
the question.)

Which means that the binary operation must be addition.

Based on the value of `Int.init()`, you conclude that addition of
integers is a "more prominent and useful" operation than multiplication?
Again, this is backwards. Rather, we know that each numerical type belongs
to multiple ring algebras; there is no basis for reckoning any as "more
useful." Since `init()` can only ever give us one value at a time, we know
that `init()` cannot give a value that is a meaningful default with respect
to any particular operation.

If I call String() I get "" which is the identity of the + String

operation.

Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

> Going to your original example, I should add: other languages provide

a version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

These two will have different signatures. The reduce you describe
returns an optional,

The statement I wrote was in JavaScript, so I'm not sure what you mean by
returning an optional. `.reduce((a, b) => a + b)` results in an error
in JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

the other one would returns the default value.

In what scenario would you prefer to propagate a default after reducing a
potential empty collection _without supplying an explicit default_ for that
operation? This would certainly violate the Swift convention of not
defaulting to zero and, I suspect, most users of Swift would not regard
that as ergonomic at all.

Fundamentally the default constructibles are useful in numerical
computations e..g. dealing with tensors.

Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols? (I should also add, FWIW, I
have never seen a generic algorithm written for integers or FP types that
has preferred the use of `T()` over `0`.)

On Sun, Dec 25, 2016 at 3:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 5:27 PM, Adam Nemecek <adamnemecek@gmail.com> >>>> wrote:

> *Which* APIs become more ergonomic?

I'll get back to this question in a second if I may. This would be a
longer discussion and I first want to make sure that before we get into the
details that there is a possibility of this being introduced (I'm asking if
violating the no zero defaults is more important than slightly more
ergonomic APIs). But to give a broad answer I think that the concept of a
zero is closely related to the concept of equality (and all the things that
build up on equality such as comparability and negation).

> 1) How does this square with Swift’s general philosophy to not
default initialize values to “zero”?

I actually wasn't aware of this philosophy. Despite this philosophy,
look at how many types actually currently implement a default constructor.

(Not a rhetorical question:) Is it well settled, either in Swift or in
C++/Rust/etc., that the value returned by a default initializer/constructor
is regarded as an identity element or zero? Is the thread that I get by
writing `let t = Thread()` some kind of zero in any reasonable sense of the
word?

Also can I ask what's the motivation behind this philosophy?
I think that in Swift, default constructibility makes complete sense
for (most?) structs, maybe less so for classes.

> 2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

This is a good point that I've considered as well but felt that for
the most part, there is one particular identity and associated operation
that is more prominent and useful than others. Furthermore, modeling
different algebras isn't mutually exclusive with writing generic algorithms
that rely on this protocol, you can always introduce some monoidic wrapper
type that defines the more appropriate default value and operation.

Do you mean to argue that for an integer the additive identity should
be considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

Going to your original example, I should add: other languages provide a
version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

On Sun, Dec 25, 2016 at 1:24 PM, Chris Lattner <clattner@apple.com> >>>>> wrote:

On Dec 25, 2016, at 12:54 PM, Adam Nemecek via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

Does enabling a lot of small improvements that make APIs more
ergonomic count as practical?

Yes, that would count as practical, but Xiaodi’s question is just as
important. *Which* APIs become more ergonomic?

Here are a couple of more questions:

1) How does this square with Swift’s general philosophy to not
default initialize values to “zero”?

2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

-Chris

On Sun, Dec 25, 2016 at 12:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>>> wrote:

On Sun, Dec 25, 2016 at 3:07 PM, Adam Nemecek <adamnemecek@gmail.com >>>>>>> > wrote:

There's a book that provides quite a bit of info on this

https://smile.amazon.com/Elements-Programming-Alexander-Step
anov/dp/032163537X?sa-no-redirect=1

They say that DefaultConstructible is one of the essential
protocols on which most algorithms rely in one way or another. One of the
authors is the designer of the C++ STL and basically the father of modern
generics.

This protocol is important for any algebraic structure that deals
with the concept of appending or addition (as "zero" is one of the
requirements of monoid). There isn't a good short answer to your question.
It's a building block of algorithms. Think about why a
RangeReplaceableCollection can provide you with a default constructor but a
Collection can't.

It's well and fine that most algorithms rely on the concept in one
way or another. Yet the Swift standard library already implements many
generic algorithms but has no DefaultConstructible, presumably because
there are other protocols that guarantee `init()` and the algorithms being
implemented don't need to be (practically speaking) generic over all
DefaultConstructible types. My question is: what practical use cases are
there for an explicit DefaultConstructible that are impractical today?

On Sun, Dec 25, 2016 at 11:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> >>>>>>>> wrote:

Can you give some other examples of generic algorithms that would
make use of this DefaultConstructible? I'm having trouble coming up with
any other than reduce.
On Sun, Dec 25, 2016 at 14:23 Adam Nemecek via swift-evolution < >>>>>>>>> swift-evolution@swift.org> wrote:

This protocol is present in C++ http://en.cppreference.com
/w/cpp/concept/DefaultConstructible as well as in Rust
std::default - Rust

It's the identity element/unit of a monoid or a zero.

The Swift implementation is very simple (I'm open to different
names)

protocol DefaultConstructible {
    init()
}

A lot of the standard types could then be made to conform to this
protocol. These include all the numeric types, collection types (array,
set, dict), string, basically at least every type that currently has a
constructor without any arguments.

The RangeReplaceableCollection protocol would inherit from this
protocol as well.

This protocol would simplify a lot of generic algorithms where
you need the concept of a zero (which shows up a lot)

Once introduced, Sequence could define an alternative
implementation of reduce where the initial result doesn't need to be
provided as it can be default constructed.

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

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

+1 to this approach. I remember I had to create it on my own for my
projects. Would be nice to have it out of the box.

···

On Mon, 26 Dec 2016 at 8:11 Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

> Yes, those particular types have initializers that take no arguments.
That does not address my question. You merely restated your initial
observation that many types in Swift have implemented `init()`.

Right, it's an empirical argument.

> I didn't think the value returned by `init()` was regarded as any sort
of zero--or even any sort of "default." In fact, some types in Foundation
have a static property called `default` distinct from `init()`.

Let's not talk about those then. This would not apply to every single type
in existence, as I've stated previously.

> It gives you something different every time. How can this be squared
with your stated motivation regarding concepts of zero and concepts of
equality?

Due to the fact that it's a resource, not a value. As I've stated above,
not all of this applies to types that are more resource-like.

> Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

This is an appeal to tradition.

> The statement I wrote was in JavaScript, so I'm not sure what you mean
by returning an optional. `.reduce((a, b) => a + b)` results in an
error in JavaScript. In Swift, such a function may also be implemented with
a precondition that the array is not empty and would not return an optional.

I was talking about their analogous swift implementations.

> Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols?

If it's implemented as either nested collections or numbers.

On Sun, Dec 25, 2016 at 6:00 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 7:30 PM, Adam Nemecek <adamnemecek@gmail.com> > wrote:

> Is it well settled, either in Swift or in C++/Rust/etc., that the value
returned by a default initializer/constructor is regarded as an identity
element or zero?

Int() == 0, String() == "" so to some extent by convention, a lot of types
have a default value as is.

Yes, those particular types have initializers that take no arguments. That
does not address my question. You merely restated your initial observation
that many types in Swift have implemented `init()`.

I didn't think the value returned by `init()` was regarded as any sort of
zero--or even any sort of "default." In fact, some types in Foundation have
a static property called `default` distinct from `init()`. In Rust, the
Default trait requires a function called `default()`, which is documented
as being useful when you want "some kind of default value, and don't
particularly care what it is."

I was asking whether there's some understanding, of which I've been
unaware, that the result of `init()` (or the equivalent in other languages)
is expected to be some sort of zero or an identity element. I'm not aware
of any evidence to that effect. Are you?

> Is the thread that I get by writing `let t = Thread()` some kind of zero
in any reasonable sense of the word?

DefaultConstructibility makes less sense for types that represent some
sort of resource but make sense for things that are values. But even in
this case, Thread() gives you a default value for example if you are
working with a resizable collection of threads.

It gives you something different every time. How can this be squared with
your stated motivation regarding concepts of zero and concepts of equality?

A better question is why does thread currently implement a default
constructor?

It's an initializer that takes no arguments, because none are needed for a
new thread. How else would you write it?

> Do you mean to argue that for an integer the additive identity should be
considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

I do. The justification is that if I call the default constructor of Int
currently, I get the value of 0.

This is backwards. Why do you believe that the value you obtain from
`init()` is intended to be an identity element at all, let alone the most
important one? (It's also circular reasoning. Since `init()` only ever
gives you one value at a time, by your reasoning it demonstrates that every
type must have one "more prominent and useful" identity, which is begging
the question.)

Which means that the binary operation must be addition.

Based on the value of `Int.init()`, you conclude that addition of integers
is a "more prominent and useful" operation than multiplication? Again, this
is backwards. Rather, we know that each numerical type belongs to multiple
ring algebras; there is no basis for reckoning any as "more useful." Since
`init()` can only ever give us one value at a time, we know that `init()`
cannot give a value that is a meaningful default with respect to any
particular operation.

If I call String() I get "" which is the identity of the + String
operation.

Or, it's what you get because that's the most trivial possible string.
Quite simply, I do not think the designer of most types that implement
`init()` have paused to wonder whether the value that you get is the
identity element associated with the most useful and prominent operation
that can be performed on that type. I certainly never have.

> Going to your original example, I should add: other languages provide a
version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

These two will have different signatures. The reduce you describe returns
an optional,

The statement I wrote was in JavaScript, so I'm not sure what you mean by
returning an optional. `.reduce((a, b) => a + b)` results in an error
in JavaScript. In Swift, such a function may also be implemented with a
precondition that the array is not empty and would not return an optional.

the other one would returns the default value.

In what scenario would you prefer to propagate a default after reducing a
potential empty collection _without supplying an explicit default_ for that
operation? This would certainly violate the Swift convention of not
defaulting to zero and, I suspect, most users of Swift would not regard
that as ergonomic at all.

Fundamentally the default constructibles are useful in numerical
computations e..g. dealing with tensors.

Can you give an example of an algorithm dealing with tensors where you
would use a `DefaultConstructible` generic over all types that have
`init()`, as opposed to working with the existing `Integer`,
`FloatingPoint`, and other numerical protocols? (I should also add, FWIW, I
have never seen a generic algorithm written for integers or FP types that
has preferred the use of `T()` over `0`.)

On Sun, Dec 25, 2016 at 3:30 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 5:27 PM, Adam Nemecek <adamnemecek@gmail.com> > wrote:

> *Which* APIs become more ergonomic?

I'll get back to this question in a second if I may. This would be a
longer discussion and I first want to make sure that before we get into the
details that there is a possibility of this being introduced (I'm asking if
violating the no zero defaults is more important than slightly more
ergonomic APIs). But to give a broad answer I think that the concept of a
zero is closely related to the concept of equality (and all the things that
build up on equality such as comparability and negation).

> 1) How does this square with Swift’s general philosophy to not default
initialize values to “zero”?

I actually wasn't aware of this philosophy. Despite this philosophy, look
at how many types actually currently implement a default constructor.

(Not a rhetorical question:) Is it well settled, either in Swift or in
C++/Rust/etc., that the value returned by a default initializer/constructor
is regarded as an identity element or zero? Is the thread that I get by
writing `let t = Thread()` some kind of zero in any reasonable sense of the
word?

Also can I ask what's the motivation behind this philosophy?
I think that in Swift, default constructibility makes complete sense for
(most?) structs, maybe less so for classes.

> 2) To your original example, it isn’t immediately clear to me that
reduce should choose a default identity. Some types (e.g. integers and FP)
belong to multiple different ring algebras, and therefore have different
identity values that correspond to the relevant binary operations.

This is a good point that I've considered as well but felt that for the
most part, there is one particular identity and associated operation that
is more prominent and useful than others. Furthermore, modeling different
algebras isn't mutually exclusive with writing generic algorithms that rely
on this protocol, you can always introduce some monoidic wrapper type that
defines the more appropriate default value and operation.

Do you mean to argue that for an integer the additive identity should be
considered "more prominent and useful" than the multiplicative identity?
I'm not aware of any mathematical justification for such a conclusion.

Going to your original example, I should add: other languages provide a
version of `reduce` that doesn't require an initial result (for instance,
JavaScript). In JavaScript, `[1, 2, 3].reduce((a, b) => a + b)` uses the
element at array index 0 as the initial result, and the accumulator
function is invoked starting with the element at array index 1. This is
precisely equivalent to having `reduce` use the additive identity as the
default initial result when + is the accumulator function and the
multiplicative identity when * is the accumulator function (with the
accumulator function being invoked starting with the element at array index
0). It does not require a DefaultConstructible protocol. What more
ergonomic solution could be implemented using a monoidic wrapper type?

On Sun, Dec 25, 2016 at 1:24 PM, Chris Lattner <clattner@apple.com> wrote:

On Dec 25, 2016, at 12:54 PM, Adam Nemecek via swift-evolution < > swift-evolution@swift.org> wrote:

Does enabling a lot of small improvements that make APIs more ergonomic
count as practical?

Yes, that would count as practical, but Xiaodi’s question is just as
important. *Which* APIs become more ergonomic?

Here are a couple of more questions:

1) How does this square with Swift’s general philosophy to not default
initialize values to “zero”?

2) To your original example, it isn’t immediately clear to me that reduce
should choose a default identity. Some types (e.g. integers and FP) belong
to multiple different ring algebras, and therefore have different identity
values that correspond to the relevant binary operations.

-Chris

On Sun, Dec 25, 2016 at 12:19 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 3:07 PM, Adam Nemecek <adamnemecek@gmail.com> > wrote:

There's a book that provides quite a bit of info on this

https://smile.amazon.com/Elements-Programming-Alexander-Stepanov/dp/032163537X?sa-no-redirect=1

They say that DefaultConstructible is one of the essential protocols on
which most algorithms rely in one way or another. One of the authors is the
designer of the C++ STL and basically the father of modern generics.

This protocol is important for any algebraic structure that deals with the
concept of appending or addition (as "zero" is one of the requirements of
monoid). There isn't a good short answer to your question. It's a building
block of algorithms. Think about why a RangeReplaceableCollection can
provide you with a default constructor but a Collection can't.

It's well and fine that most algorithms rely on the concept in one way or
another. Yet the Swift standard library already implements many generic
algorithms but has no DefaultConstructible, presumably because there are
other protocols that guarantee `init()` and the algorithms being
implemented don't need to be (practically speaking) generic over all
DefaultConstructible types. My question is: what practical use cases are
there for an explicit DefaultConstructible that are impractical today?

On Sun, Dec 25, 2016 at 11:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Can you give some other examples of generic algorithms that would make use
of this DefaultConstructible? I'm having trouble coming up with any other
than reduce.
On Sun, Dec 25, 2016 at 14:23 Adam Nemecek via swift-evolution < > swift-evolution@swift.org> wrote:

This protocol is present in C++
http://en.cppreference.com/w/cpp/concept/DefaultConstructible as well as
in Rust std::default - Rust

It's the identity element/unit of a monoid or a zero.

The Swift implementation is very simple (I'm open to different names)

protocol DefaultConstructible {
    init()
}

A lot of the standard types could then be made to conform to this
protocol. These include all the numeric types, collection types (array,
set, dict), string, basically at least every type that currently has a
constructor without any arguments.

The RangeReplaceableCollection protocol would inherit from this protocol
as well.

This protocol would simplify a lot of generic algorithms where you need
the concept of a zero (which shows up a lot)

Once introduced, Sequence could define an alternative implementation of
reduce where the initial result doesn't need to be provided as it can be
default constructed.

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

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

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

The fact that certain bit-patterns correspond to zero and that zero has

been a default in other languages is an artifact of their hardware
representation, but not necessarily one that should motivate higher level
design.

Zero isn't just a bit pattern. It's the philosophical idea of nothingness,
void, nihil. The fact that we found a common pattern between addition and
multiplication doesn't mean that nothingness has lost it's meaning.

It's a circular argument to use that as a reason for why default

constructibility should exist as a consequence of that.

Can you provide an example where a non-zero value for a value like type
makes more sense than all zeros? Also it's not circular, I need to work
with types that have a placeholder value. Most values have only one such
value.

(One example: an ORM where objects have to be associated with an

originating "context"; either the type needs to have an initializer that
takes a context, or the context is a factory that returns instances.)

In ActiveRecord, the Rails ORM, I can call User.new on a User model,
correct? I'm relatively sure most other ORMs have similar functionality

···

On Mon, Dec 26, 2016 at 12:43 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Mon, Dec 26, 2016 at 12:15 PM Adam Nemecek <adamnemecek@gmail.com> > wrote:

> The all-zero bit pattern represents the integer zero—that's not the
same as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

It represents a sensible value to initialize an int to when I want to
initialize an array of ints to a certain size. There is a reason why you
zero out memory but you don't "one out" memory.

It was previously mentioned in this thread, but Swift explicitly made the
choice to *not* initialize values to zero by default. If you want to
initialize an array of ints, you can just as easily pass the default in
that you want. If you want to do this more generically, you can still push
the responsibility to the caller, which is arguably better because it
doesn't restrict your algorithm to only those data types that conform to a
particular protocol.

The fact that certain bit-patterns correspond to zero and that zero has
been a default in other languages is an artifact of their hardware
representation, but not necessarily one that should motivate higher level
design.

> That doesn't explain why the additive identity is more special than
the multiplicative one. It just argues that it's more convenient for a
particular use case.

Because the identity associated with the default initializer as is is the
additive identity which means the default operation is addition.

I should have phrased more carefully: it doesn't explain why the additive
identity *should be* more special.

There is no "default operation" over integers. The fact that the additive
identity also happens to be the all zero bit-pattern which also happens to
be the default initialized state isn't an argument for why it *should* be
that way—it's just explaining what the current state of things is. It's a
circular argument to use that as a reason for why default constructibility
should exist as a consequence of that.

> Because encapsulation. There's a reason NSObject has a default
initializer.

That's not an encapsulation that works everywhere. Consider this: there
exist types that might have reasonable "default" values but for which the
default initializer isn't an appropriate or possible way to express it.
(One example: an ORM where objects have to be associated with an
originating "context"; either the type needs to have an initializer that
takes a context, or the context is a factory that returns instances.) By
writing an algorithm that relies on DefaultConstructible, you prohibit
those types from being used there. If the algorithm pushes the
responsibility to the caller, as Array(repeating:count:) does, then it can
work for both types. It's *more* general and more powerful than the
DefaultConstructible version.

The reason NSObject has a default initializer is because every object
inherits from it and it must have a sensible default initializer to end the
call chain, and there's nothing meaningful that needs to be parameterized
for NSObject. If you call [[NSObject alloc] init] by itself, you get back
an object that doesn't really do much. But I'm not sure what that has to do
with generic algorithms—what about all the types that extend NSObject that
don't have default initializers?

On Mon, Dec 26, 2016 at 11:57 AM, Tony Allevato <tony.allevato@gmail.com> >> wrote:

On Mon, Dec 26, 2016 at 11:43 AM Adam Nemecek via swift-evolution < >> swift-evolution@swift.org> wrote:

> For integers, 0 is an additive identity. Is there a reason it should
be given special treatment over 1, the multiplicative identity?

E.g. for statistical reasons. When I have a collection of users with age
etc it makes sense to ask what is the combined age of the collection? What
is the semantic meaning of multiplying their ages?

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

I would turn your example around—if you're interested enough in thorough
type design that you feel that a DefaultConstructible protocol would be
useful here, then I offer that a better and safer design would be to create
an "Age" value type (or, more generally, measurement types with concepts of
units) if you want compile-time safety and limiting the supported
operations to only those that are sensical. "Int" is arguably too wide a
type to represent an age in a public API because it would allow two ages to
be multiplied together, as you said.

> Mathematically, identities are associated with (type, operation) pairs,
not types alone.

Correct, however we aren't talking about mathematics, we are talking
about the implementation of a language that runs on very concrete
architectures where very concrete bit patterns mean very concrete things
that are unlikely to change any time soon.

The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

On Mon, Dec 26, 2016 at 11:35 AM, Tony Allevato <allevato@google.com> >> wrote:

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.
On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < >> swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> >> wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < >> swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mo
n-20160704/023642.html

Imho "DefaultConstructible" types can be very handy, but so far, it seems
no one has presented a single use case that is important enough to justify
the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for
for me, the question is "Does this protocol confuse users of Swift?", which
I'd answer with "yes, possibly" (unless someone comes up with a name that
is more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

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

T() as a placeholder is just as valid/invalid as any other value in T's

space.

Correct but you how do you get any other value?

···

On Mon, Dec 26, 2016 at 1:39 PM, Tony Allevato via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Dec 26, 2016 at 1:19 PM David Sweeris <davesweeris@mac.com> wrote:

On Dec 26, 2016, at 12:10, Tony Allevato <tony.allevato@gmail.com> wrote:

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution < >> swift-evolution@swift.org> wrote:

On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com> wrote:

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.

I'd agree, except sometimes you need a T, *any* T, for when you want to
create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because
it'll still crash if you try to store anything in a[i] without somehow
putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of
choosing the default value at the call site. If someone were writing a
generic algorithm around this, then why not just propagate that
responsibility out to its call site as well? That way, the algorithm isn't
making any assumptions about what the "default" value is or even if one
exists, and it doesn't impose additional requirements on the element type.
For example, the user could get the default from a static factory method,
an instance method on another object, or something else entirely.

Yeah, that's what I would use… The "filled out" example would be:

extension Array {
    public func pmap<T: DefaultInitable> (transform: (Element) -> T) -> [
T] {
        var result = Array<T>(repeating: T(), count: self.count) //Pick
a T... any T...
        for i in self.indices {
            result[i] = whateverTheConcurrentExectutionSyntaxIs(self[i],
transform)
        }
        return result
    }
}
var thisCouldTakeAWhile = Array((0...10000)).pmap {
    someReallySlowFunction($0)
}

At least I *think* that’d work... I haven’t tried it yet... Anyway,
without some way (*any* way) of getting an instance of T to fill in the
`result` array, it becomes much trickier to keep track of all the
concurrently-calculated transformed values. In this case, the semantics of
`T()` are fairly irrelevant because the semantics of the *overall
statement* is just to work around a language limitation (Swift not
having separate allocation and initialization phases), which doesn’t have
anything to do with the semantics of the initial value that got passed as
the `repeating` argument.

This looks like it's abusing T() to stand in for nil, though. Before the
result[i] assignment executes, result[i] shouldn't conceptually have a
value—you're putting a default in there because the implementation requires
it. T() as a placeholder is just as valid/invalid as any other value in T's
space.

It's a square-peg-in-a-round-hole problem—design-wise, an array of
optionals would be a better fit, but you rightly don't want to return that
from the function, and you don't want to bear the cost of converting the
array of optionals to an array of non-optionals after the computation is
complete. That makes complete sense, but perhaps *that* is the problem that
should be addressed, instead of trying to use T() to avoid it? Or
alternatively, a parallel map operation should return an array of futures
(or a collection type that itself is a future).

- Dave Sweeris

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

Mathematically, identities are associated with (type, operation) pairs, not types alone.

This conversation has put me in the column of "numeric types shouldn't have default initializers at all", personally.

I'd agree, except sometimes you need a T, any T, for when you want to create a "pre-sized" array for stuffing results into by index:
for i in ... {
    a[i] = ...
}
Simply saying "var a =[T](); a.reserveCapacity()" doesn't cut it because it'll still crash if you try to store anything in a[i] without somehow putting at least i+1 elements in the array first.

Array already has init(repeating:count:) that puts the responsibility of choosing the default value at the call site. If someone were writing a generic algorithm around this, then why not just propagate that responsibility out to its call site as well? That way, the algorithm isn't making any assumptions about what the "default" value is or even if one exists, and it doesn't impose additional requirements on the element type. For example, the user could get the default from a static factory method, an instance method on another object, or something else entirely.

Yeah, that's what I would use… The "filled out" example would be:
extension Array {
    public func pmap<T: DefaultInitable> (transform: (Element) -> T) -> [T] {
        var result = Array<T>(repeating: T(), count: self.count) //Pick a T... any T...
        for i in self.indices {
            result[i] = whateverTheConcurrentExectutionSyntaxIs(self[i], transform)
        }
        return result
    }
}
var thisCouldTakeAWhile = Array((0...10000)).pmap {
    someReallySlowFunction($0)
}
At least I think that’d work... I haven’t tried it yet... Anyway, without some way (any way) of getting an instance of T to fill in the `result` array, it becomes much trickier to keep track of all the concurrently-calculated transformed values. In this case, the semantics of `T()` are fairly irrelevant because the semantics of the overall statement is just to work around a language limitation (Swift not having separate allocation and initialization phases), which doesn’t have anything to do with the semantics of the initial value that got passed as the `repeating` argument.

This looks like it's abusing T() to stand in for nil, though. Before the result[i] assignment executes, result[i] shouldn't conceptually have a value—you're putting a default in there because the implementation requires it. T() as a placeholder is just as valid/invalid as any other value in T's space.

Pretty much, yeah.

It's a square-peg-in-a-round-hole problem—design-wise, an array of optionals would be a better fit, but you rightly don't want to return that from the function, and you don't want to bear the cost of converting the array of optionals to an array of non-optionals after the computation is complete. That makes complete sense, but perhaps *that* is the problem that should be addressed, instead of trying to use T() to avoid it? Or alternatively, a parallel map operation should return an array of futures (or a collection type that itself is a future).

Possibly correct… I think of it more as “solving the ’semantics’ problem from an unexpected direction”, which may well turn out to be an equivalent statement to yours (I don’t think I know enough about the topic to say for sure one way or the other). I believe adding those features is out-of-scope for now, though, so I was trying to illustrate a use-case that we might run into in the current language. Regarding using a `[T?]` and changing the last line to `return result.map {$0!}`, yeah, that’d absolutely work… it’s one more pass through the array than is computationally necessary, though, and some people need all the performance they can get. Someone mentioned `ManagedBuffer`… I’d forgotten that entire group of “*Buffer" types existed... While we’re waiting for certain features to come in-scope, it certainly sounds like one of them might be a better choice for this example.

- Dave Sweeris

···

On Dec 26, 2016, at 1:39 PM, Tony Allevato <tony.allevato@gmail.com> wrote:
On Mon, Dec 26, 2016 at 1:19 PM David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
On Dec 26, 2016, at 12:10, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

On Mon, Dec 26, 2016 at 11:57 AM David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Dec 26, 2016, at 11:35, Tony Allevato <allevato@google.com <mailto:allevato@google.com>> wrote:

Probably, yeah… That’s one of the types in the “*Buffer” group that I forgot existed.

- Dave Sweeris

···

On Dec 26, 2016, at 1:46 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This sounds like what UnsafeMutableRawBufferPointer was designed for, no?

> The fact that certain bit-patterns correspond to zero and that zero has
been a default in other languages is an artifact of their hardware
representation, but not necessarily one that should motivate higher level
design.

Zero isn't just a bit pattern. It's the philosophical idea of nothingness,
void, nihil

0, Never, Void, and nil are distinct in Swift. (And yes, nil is a
contraction of nihil.) I'm not sure what your argument is at this point; we
are not debating the philosophical ideal of nothingness. The concept of
nothingness is modeled in distinct ways for different purposes in Swift,
and sometimes it is not modeled at all. Simply because the philosophical
idea of nothingness is important does not mean that it is profitable to
model that concept in every conceivable place where it can be modeled in
Swift.

The fact that we found a common pattern between addition and multiplication

doesn't mean that nothingness has lost it's meaning.

> It's a circular argument to use that as a reason for why default
constructibility should exist as a consequence of that.

Can you provide an example where a non-zero value for a value like type
makes more sense than all zeros? Also it's not circular, I need to work
with types that have a placeholder value. Most values have only one such
value.

As I explained earlier, your argument _is_ circular. But it's clear now you
will not accept that being point out to you by multiple people
independently.

I would not mind `Int.init()` yielding a random integer every time. It
seems perfectly sensible to me. Were it not for security implications, I
wouldn't even mind if whatever configuration of bits were there before
allocation were the value that one got from `Int.init()`.

···

On Mon, Dec 26, 2016 at 3:52 PM, Adam Nemecek via swift-evolution < swift-evolution@swift.org> wrote:

(One example: an ORM where objects have to be associated with an
originating "context"; either the type needs to have an initializer that
takes a context, or the context is a factory that returns instances.)

In ActiveRecord, the Rails ORM, I can call User.new on a User model,
correct? I'm relatively sure most other ORMs have similar functionality

On Mon, Dec 26, 2016 at 12:43 PM, Tony Allevato <tony.allevato@gmail.com> > wrote:

On Mon, Dec 26, 2016 at 12:15 PM Adam Nemecek <adamnemecek@gmail.com> >> wrote:

> The all-zero bit pattern represents the integer zero—that's not the
same as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

It represents a sensible value to initialize an int to when I want to
initialize an array of ints to a certain size. There is a reason why you
zero out memory but you don't "one out" memory.

It was previously mentioned in this thread, but Swift explicitly made the
choice to *not* initialize values to zero by default. If you want to
initialize an array of ints, you can just as easily pass the default in
that you want. If you want to do this more generically, you can still push
the responsibility to the caller, which is arguably better because it
doesn't restrict your algorithm to only those data types that conform to a
particular protocol.

The fact that certain bit-patterns correspond to zero and that zero has
been a default in other languages is an artifact of their hardware
representation, but not necessarily one that should motivate higher level
design.

> That doesn't explain why the additive identity is more special than
the multiplicative one. It just argues that it's more convenient for a
particular use case.

Because the identity associated with the default initializer as is is
the additive identity which means the default operation is addition.

I should have phrased more carefully: it doesn't explain why the additive
identity *should be* more special.

There is no "default operation" over integers. The fact that the additive
identity also happens to be the all zero bit-pattern which also happens to
be the default initialized state isn't an argument for why it *should* be
that way—it's just explaining what the current state of things is. It's a
circular argument to use that as a reason for why default constructibility
should exist as a consequence of that.

> Because encapsulation. There's a reason NSObject has a default
initializer.

That's not an encapsulation that works everywhere. Consider this: there
exist types that might have reasonable "default" values but for which the
default initializer isn't an appropriate or possible way to express it.
(One example: an ORM where objects have to be associated with an
originating "context"; either the type needs to have an initializer that
takes a context, or the context is a factory that returns instances.) By
writing an algorithm that relies on DefaultConstructible, you prohibit
those types from being used there. If the algorithm pushes the
responsibility to the caller, as Array(repeating:count:) does, then it can
work for both types. It's *more* general and more powerful than the
DefaultConstructible version.

The reason NSObject has a default initializer is because every object
inherits from it and it must have a sensible default initializer to end the
call chain, and there's nothing meaningful that needs to be parameterized
for NSObject. If you call [[NSObject alloc] init] by itself, you get back
an object that doesn't really do much. But I'm not sure what that has to do
with generic algorithms—what about all the types that extend NSObject that
don't have default initializers?

On Mon, Dec 26, 2016 at 11:57 AM, Tony Allevato <tony.allevato@gmail.com >>> > wrote:

On Mon, Dec 26, 2016 at 11:43 AM Adam Nemecek via swift-evolution < >>> swift-evolution@swift.org> wrote:

> For integers, 0 is an additive identity. Is there a reason it should
be given special treatment over 1, the multiplicative identity?

E.g. for statistical reasons. When I have a collection of users with age
etc it makes sense to ask what is the combined age of the collection? What
is the semantic meaning of multiplying their ages?

That doesn't explain why the additive identity is more special than the
multiplicative one. It just argues that it's more convenient for a
particular use case.

I would turn your example around—if you're interested enough in thorough
type design that you feel that a DefaultConstructible protocol would be
useful here, then I offer that a better and safer design would be to create
an "Age" value type (or, more generally, measurement types with concepts of
units) if you want compile-time safety and limiting the supported
operations to only those that are sensical. "Int" is arguably too wide a
type to represent an age in a public API because it would allow two ages to
be multiplied together, as you said.

> Mathematically, identities are associated with (type, operation)
pairs, not types alone.

Correct, however we aren't talking about mathematics, we are talking
about the implementation of a language that runs on very concrete
architectures where very concrete bit patterns mean very concrete things
that are unlikely to change any time soon.

The all-zero bit pattern represents the integer zero—that's not the same
as whether it represents the best "default" value for an integer as a
higher-level concept, or whether such a default should exist in the first
place.

On Mon, Dec 26, 2016 at 11:35 AM, Tony Allevato <allevato@google.com> >>> wrote:

For integers, 0 is an additive identity. Is there a reason it should be
given special treatment over 1, the multiplicative identity? Historically
the only reason is because it has the all-clear bit pattern.

Mathematically, identities are associated with (type, operation) pairs,
not types alone.

This conversation has put me in the column of "numeric types shouldn't
have default initializers at all", personally.
On Mon, Dec 26, 2016 at 11:27 AM Adam Nemecek via swift-evolution < >>> swift-evolution@swift.org> wrote:

The elements already have an Identity, the one that you get when you
invoke the default constructor. It's 0 for Int, "" for String.

On Mon, Dec 26, 2016 at 11:24 AM, David Sweeris <davesweeris@mac.com> >>> wrote:

On Dec 26, 2016, at 11:12, Tino Heth via swift-evolution < >>> swift-evolution@swift.org> wrote:

There is an older discussion that is somewhat linked to this topic:
"Removing the empty initialiser requirement from
RangeReplaceableCollection"
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mo
n-20160704/023642.html

Imho "DefaultConstructible" types can be very handy, but so far, it
seems no one has presented a single use case that is important enough to
justify the inclusion in the stdlib.
On the other hand, I'm quite sure that there's much functionality in the
stdlib that many people consider as superfluous…

I guess adding the protocol wouldn't have a big impact on size, so for
for me, the question is "Does this protocol confuse users of Swift?", which
I'd answer with "yes, possibly" (unless someone comes up with a name that
is more intuitive).

"Identity", but, at least for many numeric types, you'd need a mechanism
for specifying which one you mean.

- Dave Sweeris

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

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

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