<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 9. Jun 2017, at 21:47, Matthew Johnson via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Jun 9, 2017, at 2:39 PM, Xiaodi Wu &lt;<a href="mailto:xiaodi.wu@gmail.com" class="">xiaodi.wu@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class="">Interesting. So you’d want `newtype Foo = String` to start off with no members on Foo?<br class=""></div></blockquote><div class=""><br class=""></div><div class="">Yeah. &nbsp;Previous discussions of newtype have usually led to discussion of ways to forward using a protocol-oriented approach. &nbsp;Nothing has gotten too far, but it usually comes up that suppressing undesired members is important.</div><div class=""><br class=""></div><div class="">It is also important to have some way to distinguish between members with a parameter of the underlying type from members that should be treated by newtype as Self parameters. &nbsp;The mechanism we have for doing that in Swift happens to be a protocol.</div></div></div></div></blockquote></div><br class=""><div class="">It’s important to note that you can create powerful quasi-subtype relationships using composition. This is going to be a little divergent, but this is WWDC week and everybody’s here, and its related to the topic of architecture with value-types.</div><div class=""><br class=""></div><div class="">The only practical difference between composition and subclassing is that you can’t add storage and have it carried along with the original “instance”. I’m not even sure that really makes sense for value types, which don’t have identity — the alternative, that a value is be composed of sub-values, makes more sense to me.</div><div class=""><br class=""></div><div class="">I’ve recently converted an entire project from using class hierarchies and stacks of protocols down to a handful of protocols and value-types. I cut the number of files in half, reduced duplicated logic, increased testability, maintainability, all kinds of good stuff. I’m pretty happy with how it turned out, so let me very briefly outline how I did it, to give a concrete example of the kinds of things you can do with composition.</div><div class=""><br class=""></div><div class="">The new data subsystem of this App is now entirely stateless - ultimately, if you look through all of the wrappers, we’re literally just passing around a handful of “let” variables (api endpoint, api key, login key, and a cache identifier), and the entire framework boils down to isolated islands of business logic written in extensions on those structs.</div><div class=""><br class=""></div><div class="">There are only two really important protocols: an ObjectStore (i.e. a cache) and an ObjectSource (i.e. the API). They have very generic methods, like “fetch”, “put” and “delete”. All of the business logic about which queries to run on the cache, and where to put the data, or how to query the correct data out of the API, is written in a collection of wrapper structs which you access to via a computed property (a kind of namespaced protocol-extension, e.g. myObjectStore.userInformation). Above the source and store (i.e. wrapping them) sits a Repository struct, which coordinates getting data from the ObjectStore, querying for that data from the ObjectSource if it doesn’t have anything (or if its expired), and returns a future (actually it’s a Reactive Observable, but any kind of future-like-object will do) encapsulating the operation.&nbsp;</div><div class=""><br class=""></div><div class="">There’s lots you can do with value-types. For example, I created a wrapper for the top-level “Session” type which dynamically checks if the session belongs to a logged-in user. There is a separate repository for public data (e.g. store locations) and private data (e.g. purchase history), and we can model all of this separation really easily in the type-system with no cognitive or computational overhead.</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><font face="Menlo" class="">/// An API session, which may or may not belong to a logged-in user.</font></div><div class=""><font face="Menlo" class="">///</font></div><div class=""><font face="Menlo" class="">struct Session {</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; typealias Identity =&nbsp;</font><font face="Menlo" class="">(loginKey: String,&nbsp;</font><span style="font-family: Menlo;" class="">cacheIdentifier: String)</span></div><div class=""><span style="font-family: Menlo;" class=""><br class=""></span></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; let configuration: SessionConfiguration // creates repository on behalf of the session, for unit-testing.</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; let identity: Identity?</font></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; let publicData: PublicDataRepository</span></div><div class=""><span style="font-family: Menlo;" class=""><br class=""></span></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; init(configuration: SessionConfiguration, identity: Identity) {</span></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; &nbsp; &nbsp; self.configuration = configuration</span></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span><font face="Menlo" class="">self.identity &nbsp; &nbsp; &nbsp;= identity</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; &nbsp; &nbsp; self.publicData &nbsp; &nbsp;= configuration.makePublicRepository()</font></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; }</span></div><div class=""><font face="Menlo" class="">}</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">/// A session which is dynamically known to belong to a logged-in user.</font></div><div class=""><font face="Menlo" class="">///</font></div><div class=""><font face="Menlo" class="">struct AuthenticatedSession {</font></div><div class=""><br class=""></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; let base: Session&nbsp;</font><span style="font-family: Menlo;" class="">// you can think of this like ‘super</span><font face="Menlo" class="">’</font></div><div class=""><span style="font-family: Menlo;" class="">&nbsp; &nbsp; let privateData: PrivateDataRepository</span></div><div class=""><span style="font-family: Menlo;" class=""><br class=""></span></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; init?(base: Session) {</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; &nbsp; &nbsp; guard let identity = base.identity else { return nil }</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; &nbsp; &nbsp; self.base &nbsp; = base</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; &nbsp; &nbsp; privateData = base.configuration.makePrivateRepository(for: identity)</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; }</font></div><div class=""><font face="Menlo" class="">}</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">/* methods which do not require authentication */</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">extension Session {&nbsp;</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; func getStoreLocations() -&gt; Future&lt;[StoreLocation]&gt; { … }</font></div><div class=""><font face="Menlo" class="">&nbsp;}</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">/* methods which require a logged-in user */</font></div><div class=""><font face="Menlo" class=""><br class=""></font></div><div class=""><font face="Menlo" class="">extension AuthenticatedSession { &nbsp;</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; func buySomething(_: ThingToBuy) -&gt; Future&lt;PurchaseReceipt&gt; { … }</font></div><div class=""><font face="Menlo" class="">}</font></div><div class=""><br class=""></div></blockquote>When it comes to storage, AuthenticatedSession not having the same memory layout as Session means you can’t store one variable that could be either — unless you box it. You can use a protocol to create a semantically-meaningful box (e.g. PublicDataProvider, with one computed property which returns the public-only “Session”), or if you can’t be bothered, you could store it as Any and dynamic-cast to handle all the kinds of values you know how to work with.<div class=""><br class=""></div><div class="">It’s a simple model, but it works, its fast, and you can do lots with it if you know how to use it. I feel that sub-typing is really something that requires identity if you want any meaningful benefits over composition.</div><div class=""><br class=""></div><div class="">- Karl<br class=""><div class=""><div class=""><br class=""></div></div></div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>