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

Daniel Leping daniel at crossroadlabs.xyz
Mon Dec 26 00:07:56 CST 2016


Yep, exactly, a to something very common. In practice users have rarely to
conform to other stuff, also these protocols are not a part of standard
library. Seriously, if we continue thinking like this we should as well get
rid of implicit constructors too.

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

> On Mon, Dec 26, 2016 at 12:55 AM, Daniel Leping <daniel at crossroadlabs.xyz>
> wrote:
>
> Ok, an example:
>
> func myFactory<T: DefaultConstructable>(c:T.Type) -> T {
> let t = T()
>
> if let ini = t as? Initializable {
> ini.initialize(self)
> }
>
> if let co = t as? ContextAware {
> co.setContext(context)
> }
>
> //etc
>
> return t
> }
>
>
> So, users will need to conform to Initializable, ContextAware, etc. But
> you insist on compiler magic for DefaultConstructible?
>
>
>
> On Mon, 26 Dec 2016 at 11:19 Daniel Leping <daniel at crossroadlabs.xyz>
> wrote:
>
> 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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161226/ab3a6c60/attachment.html>


More information about the swift-evolution mailing list