[Pitch] Add the DefaultConstructible protocol to the standard library

This sounds like what UnsafeMutableRawBufferPointer was designed for, no?

···

On Mon, Dec 26, 2016 at 4: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

Then why is Int() allowed, and why does that return 0?

···

on Sun Dec 25 2016, Xiaodi Wu<swift-evolution@swift.org>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.
Well, I challenge that premise. There are actually very few that depend
on it AFAICT, and for those that do, there's often no obvious semantics.
For example, for numbers, should default construction produce the
additive identity (0) or multiplicative identity (1), and why?

> 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
--
-Dave

Hello.

-1

In C# a `default` is needed because the way the language handles generics
and `null`. A value type in C# cannot be `null`, but a class can. So they
have to have a `default(T)`, something like:

`T someVariable = default(T);`.

I think we do not need this in Swift and currently I do not see any really
practical general use.

Regards,

Vanderlei Martinelli

···

On Mon, Dec 26, 2016 at 7: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

1 Like

> 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.

This shows that zero is a special value with special meanings in certain
contexts. That still doesn't imply that it should be a "default" value for
that type.

> 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.

I'm not trying to make the argument that there would be a better default
value than zero. I'm arguing that there should not be a "default" value at
all.

> (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

It's just an example for illustration—don't focus specifically on the ORM
part, but on the part where it's reasonable for a type to have a notion of
a "default" value that is dependent on external state/context, and
therefore cannot be initialized with init().

···

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

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"

[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

This sounds like what UnsafeMutableRawBufferPointer was designed for, no?

I can understand the desire to write an algorithm like this without going
down to unsafe territory, but yes, that's probably the right
approach—especially since the idea of a non-optional but
not-yet-initialized value is inherently unsafe.

···

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

On Mon, Dec 26, 2016 at 4: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

> 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?

The mere fact that a value happens to be *easier* to access than others of
that type does not mean that it should be the *default* value for that
type, or that the type should have *any* default value.

···

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

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

What about “Rust way”?

protocol Default {
    static var `default`: Self { get }
}

protocol Setupable {
    mutable func setup(with params: Params)
}

func setupArray<T>(type: T.Type, params: Params) -> [T]
    where T: Default & Setupable
{
    return (1...42).map { i in
        var next = T.default
        next.setup(with: params)
        return next
    }
}

default is literally a “default” value. For primitives like Int, Char, Bool,
Float etc. it is all zeros. For structs with Default fields, it’s all
defaults—unless some special semantics is given to them. For complex types,
it’s an “empty” value, the most sane value before any setup or
specification.

> 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.

I should add: that's a core principle of generic programming as put
forth by Stepanov.

···

on Sun Dec 25 2016, Xiaodi Wu <swift-evolution@swift.org> wrote:

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

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?

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

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

--
-Dave

Can you give some examples of what you used this approach to do?

···

On Sun, Dec 25, 2016 at 9:49 PM, Daniel Leping <daniel@crossroadlabs.xyz> wrote:

+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 - Rust
default/

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

Usually it's a generic function that needs to return a value from some
other function or a default value (zero) in a case of some conditions.
Optional value is an arguable solution in quite some scenarios. Afaik,
sometimes it can be used for optional resolution.

Also, generic factories. Widely used in ORM solutions.

As mentioned above, algorythmical stuff that requires Zero.

···

On Mon, 26 Dec 2016 at 8:38 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Can you give some examples of what you used this approach to do?

On Sun, Dec 25, 2016 at 9:49 PM, Daniel Leping <daniel@crossroadlabs.xyz> > wrote:

+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

That still doesn't imply that it should be a "default" value for that

type.

That type being Int?

This shows that zero is a special value with special meanings in certain

contexts.

Where certain contexts are important contexts and special meaning is an
important meanings.

I'm not trying to make the argument that there would be a better default

value than zero. I'm arguing that there should not be a "default" value at
all.

I well aware.

It's just an example for illustration—don't focus specifically on the ORM

part, but on the part where it's reasonable for a type to have a notion of
a "default" value that is dependent on external state/context, and
therefore cannot be initialized with init().

Those are resources (if they are dealing with external state). A lot of
value types have completely reasonable defaults.

···

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

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

> 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.

This shows that zero is a special value with special meanings in certain
contexts. That still doesn't imply that it should be a "default" value for
that type.

> 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.

I'm not trying to make the argument that there would be a better default
value than zero. I'm arguing that there should not be a "default" value at
all.

> (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

It's just an example for illustration—don't focus specifically on the ORM
part, but on the part where it's reasonable for a type to have a notion of
a "default" value that is dependent on external state/context, and
therefore cannot be initialized with init().

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"
The swift-evolution Archives
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

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

It's *very* rarely necessary to drop down to unsafe code for these
cases. If you can create a Sequence/Collection that (lazily) represents
the values you need in the array, you can easily initialize the array at
the point where it's needed without doing a 2-phase dance.

···

on Mon Dec 26 2016, Tony Allevato <swift-evolution@swift.org> wrote:

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

This sounds like what UnsafeMutableRawBufferPointer was designed for, no?

I can understand the desire to write an algorithm like this without going
down to unsafe territory, but yes, that's probably the right
approach—especially since the idea of a non-optional but
not-yet-initialized value is inherently unsafe.

--
-Dave

I’ve never seen anyone use the empty init for it before, and if I ever will, I’ll probably discourage it. What is it for, anyways? Isn’t `Int.allZeros` enough?

···

On 26 Dec 2016, at 18:09, David Sweeris <davesweeris@mac.com> wrote:

On Dec 26, 2016, at 09:01, Tim Vermeulen via swift-evolution <swift-evolution@swift.org> wrote:

Then why is Int() allowed, and why does that return 0?

Predictability, I'd guess. The memory has to be initialized to *something*. And, IIRC, some architectures can 0 out memory faster than filling it with some other pattern.

- Dave Sweeris

Correct. Treating protocols as "bags of syntax" is tempting (I still find myself accidentally doing it from time to time), but that's not what a Swift protocol is.

Perhaps adding syntax to express a "bag of syntax" could be discussed either as its own topic (probably phase 2, but that's not my call), or later on as part of the macro system (it does sound rather macroish, IMHO).

- Dave Sweeris

···

On Dec 28, 2016, at 14:24, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Dec 28, 2016 at 5:21 PM, Anton Zhilin <antonyzhilin@gmail.com> wrote:
What about “Rust way”?

protocol Default {
    static var `default`: Self { get }
}

protocol Setupable {
    mutable func setup(with params: Params)
}

func setupArray<T>(type: T.Type, params: Params) -> [T]
    where T: Default & Setupable
{
    return (1...42).map { i in
        var next = T.default
        next.setup(with: params)
        return next
    }
}
default is literally a “default” value. For primitives like Int, Char, Bool, Float etc. it is all zeros. For structs with Default fields, it’s all defaults—unless some special semantics is given to them. For complex types, it’s an “empty” value, the most sane value before any setup or specification.

The objection is not about the spelling. The point here is that such a protocol does not make sufficient semantic guarantees to enable any interesting generic algorithms not possible without it. As I pointed out, Rust explicitly makes no guarantees as to what the default value is and tells you that you must not care; "most sane" does not cut it as a guarantee.

What about “Rust way”?

protocol Default {
    static var `default`: Self { get }
}

protocol Setupable {
    mutable func setup(with params: Params)
}

func setupArray<T>(type: T.Type, params: Params) -> [T]
    where T: Default & Setupable
{
    return (1...42).map { i in
        var next = T.default
        next.setup(with: params)
        return next
    }
}

default is literally a “default” value. For primitives like Int, Char,
Bool, Float etc. it is all zeros. For structs with Default fields, it’s
all defaults—unless some special semantics is given to them. For complex
types, it’s an “empty” value, the most sane value before any setup or
specification.

The objection is not about the spelling. The point here is that such a
protocol does not make sufficient semantic guarantees to enable any
interesting generic algorithms not possible without it. As I pointed out,
Rust explicitly makes no guarantees as to what the default value is and
tells you that you must not care; "most sane" does not cut it as a
guarantee.

···

On Wed, Dec 28, 2016 at 5:21 PM, Anton Zhilin <antonyzhilin@gmail.com> wrote:

Right.

Anton, perhaps I missed it, but I haven’t seen any concrete rationale for your proposal. Can you give some specific code examples that become simpler with a default constructible protocol? Perhaps you’re getting pushback because there is a better way to solve the problem you’re facing.

-Chris

···

On Dec 28, 2016, at 2:24 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Wed, Dec 28, 2016 at 5:21 PM, Anton Zhilin <antonyzhilin@gmail.com <mailto:antonyzhilin@gmail.com>> wrote:
What about “Rust way”?

protocol Default {
    static var `default`: Self { get }
}

protocol Setupable {
    mutable func setup(with params: Params)
}

func setupArray<T>(type: T.Type, params: Params) -> [T]
    where T: Default & Setupable
{
    return (1...42).map { i in
        var next = T.default
        next.setup(with: params)
        return next
    }
}
default is literally a “default” value. For primitives like Int, Char, Bool, Float etc. it is all zeros. For structs with Default fields, it’s all defaults—unless some special semantics is given to them. For complex types, it’s an “empty” value, the most sane value before any setup or specification.

The objection is not about the spelling. The point here is that such a protocol does not make sufficient semantic guarantees to enable any interesting generic algorithms not possible without it. As I pointed out, Rust explicitly makes no guarantees as to what the default value is and tells you that you must not care; "most sane" does not cut it as a guarantee.

Since people keep chiming in with “Rust has this”, I figured I should give the context for what’s up with Default in Rust. Disclaimer: I wasn’t around for the actual design of this API, but I worked with it a lot. So any justification I give is mostly my own posthoc perception of the purpose it serves today. I’ll also be using Swift terminology/syntax here since there’s no interesting aspect of Rust involved in this design.

There are three major use-cases for Default, as I see it:

1) providing conditional default initializers for generic types
2) providing a standard hook for easily writing “obvious” default initializers
3) refining another protocol for one-off convenience methods

The first case is easy. I have a `Mutex<T>`, `Box<T>`, `Rc<T>`, etc. Generic types which require an instance of their generic type to exist. So of course their initializer requires a T. But it would be nice to not have to do this for types which have default constructors. So you have `extension Mutex: Default where T: Default`, and now you can do `Mutex()` where inference makes it clear what the type is.

Here there’s no need to care about the “semantics” of Default. We’re just saying “if you can init() I can too!”.

The second case is fairly Rust-specific, in that it combines with other features to make default initialization more ergonomic. Default provides a custom deriver, which makes a super convenient way to write default constructors for Plain Old Data types. #[derive(Default)] just says “yeah add a default initializer that loads up every field with its default”. Often this is done on a concrete type full of integers/optionals, in which case it’s synonymous with zeroing.

Since initializers in Swift are totally first-class, one could conceivably create this kind of Derive system without the need for protocols. Although #[derive(Default)] is generics-aware, so it can provide conditional conformances for generic types too.

The third case is the most complex (and niche). In effect, there are several places where you can make a slightly more ergonomic thing if you refine a protocol with “has a default initializer”. These default initializers are in no sense a requirement of the protocol, so including the initializer as a requirement of the protocol is incorrect. At the same time, no one really wants a bunch of adhoc DefaultConstructibleX protocols that are used by maybe one or two functions in the entire world.

So Default is used as a universal modifier that can be applied to any protocol to create DefaultConstructibleX without anyone having to actually define or know about it. It’s a kind of retroactive modelling. If your type has some kind of reasonable default value, you conform to Default and maybe someone uses it. A particular user of `X + Default` then infers by example what a reasonable Default would mean in this context.

Examples in Rust:

* H: BuildHasher + Default — Default applies the BuildHasher’s default seeding algorithm. For some algorithms this will go out to /dev/urandom, for others this will just set it to 0. That’s the call of the BuildHasher’s designer, and is hopefully made clear in its docs. However, there’s no reason why default constructibility is fundamental to a hashing algorithm. One could reasonably make the call that there isn’t a good default, and require it to be manually constructed. Possibly they could provide a couple wrappers which provide a clear default (MySecureHasher, MyWeakHasher).

This constraint is used by HashMap<K, V, H> ’s default constructor. So in a sense this is just a more complex version of the first case, but we’re definitely inferring some semantics here. If a Default implementation doesn’t exist, then one must use HashMap’s with_hasher constructor to provide an instance of BuildHasher.

* R: Rng + Default — same basic idea. Default seeding strategy so you don’t have to pass an instance of Rng. No reason why all Rng’s must be default constructible.

* T: Extend + Default — if something can be Extended and provides a default constructor, then presumably it’s some kind of collection. So default is presumed to be the empty collection. Again, Extend is more primitive then collections — one of the ends of a channel reasonably implements Extend, but default construction doesn’t make sense in that context. This is used by

partition<C>(predicate: (Item) -> Bool) -> (C, C)
  where C: Extend + Default

which is basically just:

var yes = C()
var no = C()

for x in self {
  if predicate(x) {
    yes.extend(x)
  } else {
    no.extend(x)
  }
}

return (yes, no)

This is used similarly for unzip, which converts Iterator<(A, B)> to (CollectionOfA, CollectionOfB)

This case represents a situation where the Rust and Swift devs have diverged a bit philosophically. There’s a tendency in the Rust community to make small “lego” protocols which you snap together to get the semantics you want on the off chance there’s some weird type that doesn’t fit the mold. Swift tends to try to provide more fleshed out hierarchies.

This is partially influenced by the fact that fancy hierarchies are a lot easier to write and work with in Swift. Swift has much better support for retroactive modelling and crazy super-protocol-requirement-filling extensions. Defining things like Collection correctly in Rust has also generally been regarded as “needs some kind of higher kinded types” to handle the lifetime variables on Iterators.

All in all, I don’t really have an opinion on whether Default makes sense for Swift. Haven’t thought about it all that much. Swift is still missing the features that make case 1 and 2 even viable motivations (conditional conformance and derive annotations), and I believe already mandates default initializers in several of the cases where 3 is relevant.

Usually it's a generic function that needs to return a value from some
other function or a default value (zero) in a case of some conditions.
Optional value is an arguable solution in quite some scenarios. Afaik,
sometimes it can be used for optional resolution.

Right, I'd agree that Optional is the idiomatic way to do it. Afaict,
there's not much you can do with a default value that you couldn't with
nil, unless you have some guarantee as to _what_ that default is; however,
I'd expect that in every case that you can rely on a guarantee about a
default value which would be more useful than nil, it's going to require
more specific knowledge of your type than an all-encompassing
`DefaultConstructible` can provide.

Also, generic factories. Widely used in ORM solutions.

Can you elaborate on this? Why is Optional not a solution here?

As mentioned above, algorythmical stuff that requires Zero.

I'm still not convinced there exist credible use cases that need to be
generic over both collections and floating point, for instance. In fact, in
my experience, there are few math-heavy algorithms where one can ignore
even the distinction between integers and binary floating point. By the
time you get down to matrix math, you start to run into difficulties that
require separate implementations for Float and Double.

···

On Sun, Dec 25, 2016 at 10:18 PM, Daniel Leping <daniel@crossroadlabs.xyz> wrote:

On Mon, 26 Dec 2016 at 8:38 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Can you give some examples of what you used this approach to do?

On Sun, Dec 25, 2016 at 9:49 PM, Daniel Leping <daniel@crossroadlabs.xyz> >> wrote:

+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 - Rust
default/

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

Ok, an example from ORM. You have an entity factory with a virtual (read,
overloadable in the subclasses) method populating the properties.
DefaultConstructable is a great choice here. Otherwise you will have to
force the users of your ORM to implement a certain protocol, which you most
probably would like to avoid.

In general I think the best showcase is generic factories.

···

On Mon, 26 Dec 2016 at 9:02 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Dec 25, 2016 at 10:18 PM, Daniel Leping <daniel@crossroadlabs.xyz> > wrote:

Usually it's a generic function that needs to return a value from some
other function or a default value (zero) in a case of some conditions.
Optional value is an arguable solution in quite some scenarios. Afaik,
sometimes it can be used for optional resolution.

Right, I'd agree that Optional is the idiomatic way to do it. Afaict,
there's not much you can do with a default value that you couldn't with
nil, unless you have some guarantee as to _what_ that default is; however,
I'd expect that in every case that you can rely on a guarantee about a
default value which would be more useful than nil, it's going to require
more specific knowledge of your type than an all-encompassing
`DefaultConstructible` can provide.

Also, generic factories. Widely used in ORM solutions.

Can you elaborate on this? Why is Optional not a solution here?

As mentioned above, algorythmical stuff that requires Zero.

I'm still not convinced there exist credible use cases that need to be
generic over both collections and floating point, for instance. In fact, in
my experience, there are few math-heavy algorithms where one can ignore
even the distinction between integers and binary floating point. By the
time you get down to matrix math, you start to run into difficulties that
require separate implementations for Float and Double.

On Mon, 26 Dec 2016 at 8:38 Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Can you give some examples of what you used this approach to do?

On Sun, Dec 25, 2016 at 9:49 PM, Daniel Leping <daniel@crossroadlabs.xyz> > wrote:

+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

I’ve never seen anyone use the empty init for it before, and if I ever
will, I’ll probably discourage it. What is it for, anyways? Isn’t
`Int.allZeros` enough?

Well, that interface has to do with Int's role as a
BitwiseOperationsType, and is going away (see
https://github.com/apple/swift/pull/3796/commits/b9c910a2b94af461fc5de321cf12bf1cf45cb234\)

···

on Mon Dec 26 2016, Tim Vermeulen <swift-evolution@swift.org> wrote:

On 26 Dec 2016, at 18:09, David Sweeris <davesweeris@mac.com> wrote:

On Dec 26, 2016, at 09:01, Tim Vermeulen via swift-evolution <swift-evolution@swift.org> wrote:

Then why is Int() allowed, and why does that return 0?

Predictability, I'd guess. The memory has to be initialized to
*something*. And, IIRC, some architectures can 0 out memory faster
than filling it with some other pattern.

- Dave Sweeris

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

--
-Dave