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

Tony Allevato tony.allevato at gmail.com
Mon Dec 26 16:45:12 CST 2016


On Mon, Dec 26, 2016 at 2:21 PM Adam Nemecek <adamnemecek at gmail.com> wrote:

> > Even though the empty string is the multiplicative identity for strings,
> it's not an "origin" from which other strings can be said to have a
> distance from.
>
> Imagination is the limit. You can create a sequence of string permutations
> and assign integers to each permutation. Distance between "ab" and "abc" is
> the same as the distance between their permutation numbers. Whether this
> distance makes sense for strings is another question.
>

But that's entirely arbitrary—strings already have well-defined equality
and total ordering without defining distance. The fact that one can craft a
definition of distance that is consistent with the system doesn't mean that
distance is a *requirement* for equality/comparability to be defined—a
total ordering is simply a binary relation on the set with the appropriate
properties.


>
>
> On Mon, Dec 26, 2016 at 2:13 PM, Tony Allevato <tony.allevato at gmail.com>
> wrote:
>
>
>
> On Mon, Dec 26, 2016 at 1:59 PM Adam Nemecek via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> > Huh? You just said that one usage scenario was the allocation of
> buffers of known size. If you're unsatisfied with the API for that, you're
> welcome to propose better ones. The point is that this is not a convincing
> use case, as you claim, for `DefaultConstructible`, but rather for better
> buffer APIs.
>
> I've brought up several situation where I'd use these. I don't want a
> better buffer API, I want a way of expressing myself in a way that seems
> natural to me.
>
> > There is no nexus between comparability and "some sort of origin or
> zero" from which to measure absolute distance, as you state.
>
> There is. Elements are equal if the distance between them is zero. Why
> does Comparability also require Equatability?
>
>
> Many data types don't have a notion of distance, but still define a total
> ordering, which I think was the original point. What would be the distance
> between two strings? If "ab" < "abc", what is the distance between them?
> Even though the empty string is the multiplicative identity for strings,
> it's not an "origin" from which other strings can be said to have a
> distance from.
>
>
>
> > Ooh boy. There is no worldview in music that "doesn't say anything
> about intervals," I can assure you of that.
>
> https://github.com/midiguchi/midiguchi
>
> Do you see the zip operation? Could you tell me what it does? Note that I
> have no relationship to this project. I can find more if I really try. So
> now there is a precedence, so what's next? Are you going to tell me that
> this doesn't count? Why not?  I was aware of this library even before this
> discussion btw.
>
> > No, I mean that you are incorrect.
>
> Well if you say so it must be true. Lol.
>
> > What closure property? My question was, why are you not using
> `Strideable`? You are describing its semantics. I'm not sure what closure
> you are referring to.
>
> Algebraic closure.
>
> > As I explained earlier, your argument _is_ circular. But it's clear now
> you will not accept that being point out to you by multiple people
> independently.
>
> It's not circular, it's I use data point to point out that this is a
> common pattern.
>
>
>
> On Mon, Dec 26, 2016 at 1:43 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> On Mon, Dec 26, 2016 at 4:28 PM, Adam Nemecek <adamnemecek at gmail.com>
> wrote:
>
> > `ManagedBuffer` is the standard library base class that offers
> facilities for managing buffers. If there's a concrete use case that isn't
> served, then the argument would be to improve `ManagedBuffer` or to design
> other types or protocols for that use case, not to add a protocol to
> conform every type that implements `init()`.
>
> I'd prefer not to deal with raw storage unless necessary.
>
>
> Huh? You just said that one usage scenario was the allocation of buffers
> of known size. If you're unsatisfied with the API for that, you're welcome
> to propose better ones. The point is that this is not a convincing use
> case, as you claim, for `DefaultConstructible`, but rather for better
> buffer APIs.
>
>
> > The distance between two values of type T does not itself need to be of
> type T,
>
> Never said otherwise.
>
> > Moreover, one can have distances being strideable opaque types that
> can't even be initialized
>
> You sure can. Doesn't disprove any of my points.
>
>
> Sure it does. You asserted that where a type is comparable, it "generally"
> makes sense to measure distance from "some sort of origin or zero." And I'm
> showing you why it does not generally make sense at all. There is no nexus
> between comparability and "some sort of origin or zero" from which to
> measure absolute distance, as you state.
>
> > This example does not make sense, computationally or musically.
>
> You mean that it does not make any sense to you.
>
>
> No, I mean that you are incorrect.
>
>
> I have two midi streams (and they are midi) and I want to use one midi
> note to transpose the other.
>
>
> There is no such concept in music theory as "using one note to transpose
> the other." Sorry.
>
>
> I'm pretty sure that I can find a machine that does this in hardware if I
> really try. Does the fact that such machine might exist imbue the concept
> of midi addition with any meaning?
>
> > You are describing a `Strideable` type. That is already in the stdlib.
> If you want to use `+` to denote `advanced(by:)`, that's easy to add by
> extension. I'm not sure why you decided to reinvent it and call it
> `Addable`.
>
> I don't always need the closure property.
>
>
> What closure property? My question was, why are you not using
> `Strideable`? You are describing its semantics. I'm not sure what closure
> you are referring to.
>
> > Doubling the frequency shifts the pitch of a note by an octave;
> tripling the frequency gets you an octave + a perfect fifth. If you work
> through the mathematics behind this, you'll understand the issues behind
> equal temperament and well temperament.
>
> You are not multiplying pitch by pitch but pitch by number. Pitch + Pitch
> makes complete sense if you accept the midi worldview which doesn't say
> anything about intervals
>
>
> Ooh boy. There is no worldview in music that "doesn't say anything about
> intervals," I can assure you of that.
>
>
> and you realize that the computational distinction between the two is
> tenuous at best. But this discussion is neither here, nor there.
>
> On Mon, Dec 26, 2016 at 1:09 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> On Mon, Dec 26, 2016 at 1:50 PM, Adam Nemecek via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> > Equatable is *very* different.  It has a whole page of semantics.
>
> DefaultConstructible comes in handy when you want to write an algorithm
> that works with a constant sized buffer and you need to initialize the
> buffer to some default values that will be replaced once you have actual
> data. Or some objects can initialize themselves from static data (e.g. each
> object has a sequential id and uses a counter that it increments in init).
> But in all cases, you want to make sure that the array is statically sized.
>
>
> `ManagedBuffer` is the standard library base class that offers facilities
> for managing buffers. If there's a concrete use case that isn't served,
> then the argument would be to improve `ManagedBuffer` or to design other
> types or protocols for that use case, not to add a protocol to conform
> every type that implements `init()`.
>
>
> For me, it also has some implicit meaning of a zero which I agree might be
> a stretch in general but this is more explicit in cases where types are
> comparable. Order theory requires a bottom or zero for a reason.
> Fundamentally, if two elements are comparable, it makes sense to ask what
> is the distance between them. And the magnitude of these elements is
> generally measured as a distance from some sort of origin or zero.
>
>
> Careful, total ordering does not require a notion of an origin; not at
> all. The distance between two values of type T does not itself need to be
> of type T, and there need be no value of type T that represents any sort of
> "zero." Moreover, one can have distances being strideable opaque types that
> can't even be initialized (i.e. distances can be of a type U such that two
> values can have a relative distance between them, but `U.init()` isn't
> public).
>
> > Protocols (a.k.a. concepts) are not just bags of syntax; unless you can
> attach semantics to the operations,  you can't write useful generic algorithms
> against them.  So we shouldn't have DefaultConstructible for
> the same reason we shouldn't have “Plusable” to represent something that
> lets you write x + x.
>
> Haha, I totally have an Addable protocol. Out of curiosity why is it bad?
> My use case is for example a struct that's fundamentally a wrapper around
> some numerical value (int) and not all numerical operations make sense but
> e.g. addition makes total sense. E.g. a midi note event where addition
> gives you a transposition but multiplication doesn't make sense.
>
>
> This example does not make sense, computationally or musically. Firstly,
> transposition is the shifting of pitch by an _interval_; if `Self` is a
> MIDI note (as you imply below), transposition cannot be by addition of type
> `Self` but rather of `Self.Stride`. Secondly, you can absolutely multiply a
> note by an integer factor. Doubling the frequency shifts the pitch of a
> note by an octave; tripling the frequency gets you an octave + a perfect
> fifth. If you work through the mathematics behind this, you'll understand
> the issues behind equal temperament and well temperament.
>
> In this case the default value (zero) is the value that results in no
> transposition. And if I extend this, what if I have two equally sized
> arrays of midi events where one represents a transposition and the other
> represents the notes I'm transposing and I want to combine them to produce
> the transposed notes, I can write this algorithm as
>
> extension Collection where Iterator.Element: Addable {
>
>         func transpose(_ other: Self) -> [Iterator.Element] {
>
>             assert(count == other.count)
>
>             return zip(self, other).map { $0 + $1 }
>
>     }
>
> }
> I'm not sure if this example is too concrete and specific to my needs but
> I've been trying to use Swift as a language for making these little
> algebras with a pretty good degree of success but some things like this
> would be pretty helpful I think.
>
>
> You are describing a `Strideable` type. That is already in the stdlib. If
> you want to use `+` to denote `advanced(by:)`, that's easy to add by
> extension. I'm not sure why you decided to reinvent it and call it
> `Addable`.
>
> On Mon, Dec 26, 2016 at 9:29 AM, Dave Abrahams via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>
> on Mon Dec 26 2016, Jonathan Hull <swift-evolution at swift.org> wrote:
>
> > Just because something is simple, doesn’t mean it isn’t important.  You
> can do a lot with ‘return
> > T()’ that you can’t do without it (namely make a T).
>
> Sure, but the question remains: *should* you make a T in those
> circumstances?  Maybe you
>
> With DefaultConstructible, you don't know anything about the value of
> this T.  There is nothing you can do with it, reliably.  If the default
> constructability requirement is part of some larger protocol like
> RangeReplaceableCollection, then you can say things like, “this makes an
> empty collection,” and “a default-constructed instance is equivalent to an
> instance on which you've called removeAll.”  That doesn't argue for
> factoring the init() out into its own protocol.  It argues for including
> the init() requirement in every protocol where it forms an important
> part of the protocol's semantic basis operations.
>
> > Equatable is similar.  Semantically, it just lets you ask if two
> instances of the same type are
> > equal.
>
> Equatable is *very* different.  It has a whole page of semantics.  Read
> from “Equality implies substitutability” to the end of the page at
> http://swiftdoc.org/v3.0/protocol/Equatable/.
>
> > The fact that it only does one thing doesn’t mean it isn’t useful or
> > necessary as a small part of a lot of different algorithms.
> >
> > I find I use T() most often in factory or builder patterns, but any
> creational pattern may need it.
> > It is also often used together with other protocols.  The code is all
> pretty boring…
> >
> >       func hasOptionalParam( a: T = T() ) {} //The caller can pass in
> > a specific thing, or just leave out the parameter to use a vanilla one
> > or
> >
> >       var t = T()
> >       t.somethingFancy() //Provided by unrelated protocol
> >       t.moreFancy()
> >       return t
> >
> > or
> >
> >       var t = T()
> >       if t is SomeOtherProtocol {
> >               //Do something fancy
> >       }
> >       if t is YetAnotherProtocol {
> >               //Do something else fancy
> >       }
> >       return t
> >
> > All of the “fancy stuff” will be done by conforming to other protocols,
> but those protocols may have
> > nothing to do with creation (nor should they).  There is nothing wrong
> with requiring conformance to
> > multiple protocols...
>
> No, there isn't.  There *is* something wrong with slicing meaningful
> protocols up into bits that have only syntactic value, though.  I
> suspect that's what's going on here.
>
> >
> >
> > Thanks,
> > Jon
> >
> >> On Dec 26, 2016, at 7:10 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> >>
> >> The question still remains unanswered, what generic algorithms are
> >> enabled by having such a protocol? After a long chain, the answer so
> >> far is `return T()`. Indeed, afaict, the semantics you are proposing
> >> would explicitly limit us to that.
> >>
> >>
> >> On Mon, Dec 26, 2016 at 09:32 Jonathan Hull
> >> <jhull at gbis.com <mailto:jhull at gbis.com>> wrote:
> >> My two cents:
> >> 1) T() should NOT have anything to do with zero or even
> >> “default". (If we need semantic zero, create a protocol with a .zero
> >> static func/var)
> >> 2) This comes up enough in my programming, and is such a fundamental
> >> concept, that T() probably DOES deserve special treatment in the
> >> form of a protocol
> >> 3) The semantics of that protocol would be “Things which can be
> >> created without any additional information beyond their Type”
> >> 4) We should keep working on the name
> >>
> >> As to whether the protocol needs to be implicit… I am unsure.  It
> >> may be enough to have the standard library + cocoa types conform
> >> where appropriate.  On the other hand, I can’t think of any type
> >> having T() which would not fit the above semantics… and I would
> >> guess around 85~90% of types have it, so it may be worth the trouble
> >> to make it implicit in this specific case.  I am on the fence, but
> >> would probably lean against making it implicit.
> >>
> >> Thanks,
> >> Jon
> >>
> >>
> >>> On Dec 25, 2016, at 11:28 PM, Daniel Leping via swift-evolution
> >>> <swift-evolution at swift.org
> >>> <mailto:swift-evolution at swift.org>>
> >>> wrote:
> >>>
> >>> It's not a matter of probability, but rather of certainty. Please.
> >>>
> >>> On Mon, 26 Dec 2016 at 12:56 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 2:19 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto:daniel at crossroadlabs.xyz>>
> >>> wrote:
> >>> I totally agree Swift is an opinionated language and it's good.
> >>>
> >>> Also I have been thinking of DefaultConstructable vs reflection for
> >>> generic factories and I would prefer to stick to the protocol as it
> >>> gives compile time type safety check. With reflection the only way
> >>> is to through an exception if there is no init. So again +1 pro to
> >>> DefaultConstructable.
> >>>
> >>> Well, you can't argue both ways. Either so many types implement
> >>> `init()` that it is unusually onerous to type, in which case you
> >>> will gain nearly nothing from compile-time checks, or not so many
> >>> types implement `init()`, and you can conform those types to a
> >>> protocol by yourself :)
> >>>
> >>>
> >>> On Mon, 26 Dec 2016 at 12:32 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 1:48 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto:daniel at crossroadlabs.xyz>>
> >>> wrote:
> >>> Well, AnyObject exists on Linux with no bridging. Still it's
> IMPLICITELY conformed by all classes.
> >>>
> >>> What you say is just another approach to the same issue and we can
> >>> argue for eternity. However, I am very positive with syntactic
> >>> sugar and this one falls exactly to sugar category. Make people
> >>> lifes easier ;)
> >>>
> >>> Moreover it will never ever do any harm.
> >>>
> >>> Adding an easy way to get another set of frameworks/approaches/etc
> >>> (proven by time, btw) on board sounds very appealing to me. I wish
> >>> to see Swift a very diverse ecosystem and this Pitch serves exactly
> >>> this goal.
> >>>
> >>> Yes, we should let others chime in on this issue. I will just end
> >>> by saying that I've always appreciated how the core team has been
> >>> very careful and thoughtful about certain precepts, and how they've
> >>> stuck to the idea that Swift is an _opinionated_ language.
> >>>
> >>> In particular, I appreciate that there's a huge amount of thought
> >>> put into semantic meaning. The notion that protocols should carry
> >>> semantics has been adhered to very strictly. This is why I think
> >>> this proposal does do harm, because it explicitly rejects that very
> >>> important idea, one that can only be upheld by people and not
> >>> compilers.
> >>>
> >>> (Another semantic distinction observed in Swift is that a boolean
> >>> value has semantic meaning and is not just a bit; this is why, for
> >>> instance, the FloatingPoint protocols define an `enum
> >>> FloatingPointSign { case plus, minus }`--because floating point
> >>> sign has different _semantics_ from a Bool.)
> >>>
> >>> Let's just see if it gets any more positive votes.
> >>>
> >>> On Mon, 26 Dec 2016 at 12:10 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 1:21 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto:daniel at crossroadlabs.xyz>>
> >>> wrote:
> >>> I believe you're confusing in-class factory methods with factory
> pattern.
> >>>
> >>> Factories can be separate objects and it's a very different situation.
> >>>
> >>> Fair, but I understand both to fall under the umbrella of "any
> >>> factory pattern" and just wanted to point out that at least some of
> >>> those patterns seem to be discouraged :)
> >>>
> >>> In any case, I think it's fair to say that the question "does this
> >>> type implement `init()`?" is properly a reflection question and not
> >>> a protocol conformance question: the answer provides no semantic
> >>> guarantees whatsoever about the value that you get from `init()`,
> >>> and in your use case you do not care and simply want to invoke the
> >>> initializer and return what you get from it. Now, in a perfect
> >>> world where the reflection facilities that Swift provided were
> >>> essentially free of performance cost, would you object to that
> >>> characterization?
> >>>
> >>> You're certainly right that `AnyObject` has magic. It's rather
> >>> obvious that Obj-C bridging is non-negotiable for Swift, and of
> >>> course a bridged type is all sorts of different under the hood from
> >>> a native type. I'm going to take a wild guess that no other use
> >>> case would pass that high bar for magic.
> >>>
> >>>
> >>> On Mon, 26 Dec 2016 at 11:46 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 1:10 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto:daniel at crossroadlabs.xyz>>
> >>> wrote:
> >>> I'm giving a wider range, which is about ANY factory pattern
> >>> related stuff. Doesn't look to be narrow to me.
> >>>
> >>> I thought factory methods were regarded as undesirable in Swift?
> >>> One of the stated reasons for failable initializers was: "Failable
> >>> initializers eliminate the most common reason for factory methods
> >>> in Swift... Using the failable initializer allows greater use of
> >>> Swift’s uniform construction syntax, which simplifies the language
> >>> by eliminating the confusion and duplication between initializers
> >>> and factory methods."
> >>> <https://developer.apple.com/swift/blog/?id=17
> >>> <https://developer.apple.com/swift/blog/?id=17>>
> >>>
> >>>
> >>> On Mon, 26 Dec 2016 at 11:38 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 12:58 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto:daniel at crossroadlabs.xyz>>
> >>> wrote:
> >>> Well, reflection is a huge performance drop. Protocol conformance is
> way better.
> >>>
> >>> I'm not sure how huge it would be in the grand scheme of things; in
> >>> your example, you are still evaluating a train of protocol
> >>> conformances and casting at runtime. Of course, compiler magic can
> >>> be fast, but I still don't see how this is a "very common use case"
> >>> (as you write) that would justify magic equivalent to that for
> >>> Objective-C bridging, which is what you're saying it should be. If
> >>> `DefaultConstructible` is useful only when it's magic and the
> >>> specific use case is dependency injection/inversion of control,
> >>> then we're getting very specialized here.
> >>>
> >>>
> >>> On Mon, 26 Dec 2016 at 11:26 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto:xiaodi.wu at gmail.com>> wrote:
> >>> On Mon, Dec 26, 2016 at 12:50 AM, Daniel Leping
> >>> <daniel at crossroadlabs.xyz
> >>> <mailto: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).
> >>>
> >>> Well, this sounds very different from Adam's proposal, where he
> >>> proposes semantic meaning for `init()` that, as he described, means
> >>> that it cannot apply to every type that implements
> >>> `init()`. However, he also just said that he thinks that all types
> >>> with `init()` should conform, so I guess I'm confused which way
> >>> that is.
> >>>
> >>> At base, you want a way of knowing if a type has `init()`. That
> >>> sounds like reflection to me, not protocol conformance. For the
> >>> record, I look forward to the day when AnyObject magic is removed;
> >>> I assume it is coming eventually.
> >>>
> >>>
> >>> On Mon, 26 Dec 2016 at 11:14 Xiaodi Wu
> >>> <xiaodi.wu at gmail.com
> >>> <mailto: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
> >>> <mailto: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
> >>> <mailto:swift-evolution at swift.org>>
> >>> wrote:
> >
> > _______________________________________________
> > 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/b6806fdb/attachment.html>


More information about the swift-evolution mailing list