[swift-evolution] [Pitch] Add the DefaultConstructible protocol to the standard library

Daniel Leping daniel at crossroadlabs.xyz
Sun Dec 25 23:50:03 CST 2016


I'm not arguing for implicit conformance in general, but I'm telling that
DefaultConstructable is the same basic level as AnyObject, which is
conformed implicitly.

Shortly, I'm against implicit conformance in general. I'm positive with
"automatic compiler magic" conformance to DefaultConstructable for any
object having a default constructor as it really is a very basic stuff.
Otherwise you will have to add explicit conformance to it in almost every
class of yours (annoying).


On Mon, 26 Dec 2016 at 11:14 Xiaodi Wu <xiaodi.wu at gmail.com> wrote:

> On Mon, Dec 26, 2016 at 12:43 AM, Daniel Leping via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Thank you, Adam!
>
>
> Wait, are you arguing for implicit conformance or not?
>
> On Mon, 26 Dec 2016 at 11:12 Adam Nemecek via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> > Swift doesn't do implicit conformance.  It always has to be declared
> explicitly.  I'm pretty sure Doug Gregor can explain why better than I
> could.
>
>
> I don't think Daniel was arguing for implicit conformance, he's saying
> that if it makes sense for an object to have a default constructor, it
> makes sense for it to conform to the protocol which I agree with 100%.
>
> On Sun, Dec 25, 2016 at 9:17 PM, Dave Abrahams via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>
>
>
> on Sun Dec 25 2016, Daniel Leping <swift-evolution at swift.org> wrote:
>
>
>
>
>
> > You are right, usually it's required to implement a protocol which is
> not a
>
>
> > good approach. The best is plain objects which can be used independently
> of
>
>
> > ORM if needed (as DTOs, i.e.).
>
>
> >
>
>
> > I was thinking of DefaultConstructable as a protocol automatically
> applied
>
>
> > to any class/struct having a default init, which is really logical for
>
>
> > me.
>
>
>
>
>
> Swift doesn't do implicit conformance.  It always has to be declared
>
>
> explicitly.  I'm pretty sure Doug Gregor can explain why better than I
>
>
> could.
>
>
>
>
>
> > On Mon, 26 Dec 2016 at 9:41 Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>
> >
>
>
> >> On Sun, Dec 25, 2016 at 10:50 PM, Daniel Leping
>
>
> >> <daniel at crossroadlabs.xyz>
>
>
> >> wrote:
>
>
> >>
>
>
> >> 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.
>
>
> >>
>
>
> >>
>
>
> >> Sorry--I'm not very familiar with using Swift for ORM purposes. Why do
> you
>
>
> >> want to avoid having your users conform to a certain protocol? Wouldn't
> the
>
>
> >> users of your ORM have to conform to `DefaultConstructible` then? I'm
>
>
> >> looking at Swift ORMs, and all require users to conform to a protocol or
>
>
> >> inherit from a base class, typically named `Model` or similar. From a
> quick
>
>
> >> Google search:
>
>
> >>
>
>
> >> https://vapor.github.io/documentation/fluent/model.html
>
>
> >> https://github.com/blitzagency/amigo-swift
>
>
> >>
>
>
> >>
>
>
> >> In general I think the best showcase is generic factories.
>
>
> >>
>
>
> >> On Mon, 26 Dec 2016 at 9:02 Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
>
> >>
>
>
> >> On Sun, Dec 25, 2016 at 10:18 PM, Daniel Leping
>
>
> >> <daniel at 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 at 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 at 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 at 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 at gmail.com> wrote:
>
>
> >>
>
>
> >> On Sun, Dec 25, 2016 at 7:30 PM, Adam Nemecek <adamnemecek at 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 at gmail.com> wrote:
>
>
> >>
>
>
> >> On Sun, Dec 25, 2016 at 5:27 PM, Adam Nemecek <adamnemecek at 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?
>
>
> >>
>
>
> >>
>
>
> >>
>
>
> > _______________________________________________
>
>
> > swift-evolution mailing list
>
>
> > swift-evolution at swift.org
>
>
> > https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> >
>
>
>
>
>
> --
>
>
> -Dave
>
>
>
>
>
> _______________________________________________
>
>
> swift-evolution mailing list
>
>
> swift-evolution at swift.org
>
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
>
> _______________________________________________
>
> swift-evolution mailing list
>
> swift-evolution at swift.org
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
> _______________________________________________
>
>
> swift-evolution mailing list
>
>
> swift-evolution at swift.org
>
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161226/ac70c2cb/attachment-0001.html>


More information about the swift-evolution mailing list