I hope my original "feature
request" at the beginning falls into such category.
Nearly all of the data structures have been ported to ClojureScript. It's not clear to me that we needed traits at any point.David
Until it has all been factored into protocols that make it easy for people to implement their own data structures that hook into Clojure's built-in functions, I'd say the jury's still out. Currently, Clojure uses a mixture of interfaces and abstract base classes, and some things are very hard to derive and adapt. For example, I've been waiting on changes for a couple of years that would allow me to hook priority maps into subseq. Currently, subseq is implemented in terms of an interface that makes too many assumptions about how the data structure works. I just don't think you can judge the success of inheritance-less records and protocols until that sort of work is done.
I just have. This is a nice work. There is a lot of repetitions though.
>Hi David,
> Have you actually looked at the data structure implementations in
> ClojureScript? The work is done. It's all protocols.
>
I just have. This is a nice work. There is a lot of repetitions though.
> ClojureScript? The work is done. It's all protocols.Hi David,
>
I just have. This is a nice work. There is a lot of repetitions though.
For example:
- IEquiv
(-equiv [coll other] (equiv-sequential coll other))
- IHash
(-hash [coll] (caching-hash coll hash-coll __hash))
(where caching-hash has to be a macro)
- ICounted
(-count [coll] count)
- ILookup
(-lookup [coll k] (-nth coll k nil))
(-lookup [coll k not-found] (-nth coll k not-found))
I just have. This is a nice work. There is a lot of repetitions though.
Thanks Nicolas for putting together these examples. This is exactly what I've been talking about. Whenever someone talks about the issue of developing reusable partial implementation of protocols, the answer is always, "You can do that easily in Clojure by merging mappings." But in practice, I haven't seen many examples of anyone actually doing that. If people who are writing big systems (e.g., Clojurescript) aren't actually providing mergeable mappings, then writing new implementations for these protocols becomes a big copy-paste hackjob.
We need to figure out a way to make reusable partial implementations the norm.
>Well if you want to amend all of them at the same time, you have to
> And now any of those implementations can easily change at anytime without
> any external considerations. So what's the problem?
modify all of them.
On your advice of using extend-type: maybe I am wrong but I think it
has a performance cost.
I am not sure for the 1%, but the good metric is to look at the
percentage of deftypes, not the whole code base.
I think in a typical program, deftype already amount at less than 10%
of the code.
I wrote some code with a lot of different types implementing the same
interfaces and that are made
by composing different parts.
I ended up splitting aspects in different types and use composition
and "fake" protocols to implement
cross-cutting aspects.
I would have loved to have traits to do that
instead: would have been easier, more performant
and more natural to read.
> Same here--I can count on one hand the number of times I've wanted to
> implement polymorphism in three and a half years. Every time
> multimethods have worked great for the task. If you had polymorphism
> in a tight loop they might not suffice, but the decision to abandon
> multimethods should only be made after thorough benchmarking.
The point is not whether deftype is useful or not. It is in the
language so it must be useful, even it it is rarely.
The point is whether it is an expressive construct or not.
And it is not expressive enough to my taste.
The point is not whether deftype is useful or not. It is in the
language so it must be useful, even it it is rarely.
The point is whether it is an expressive construct or not.
And it is not expressive enough to my taste.
On the other hand, defrecord/deftype encourage you to write your protocol implementation in a way that cannot be reused unless you very specifically plan for it (factoring the protocol implementation into a separate map) and use a coding style that (might?) be less efficient (i.e., adding the protocol implementation via extend rather than directly in defrecord). This is not so good and potentially limits the value of protocols for developing complex systems.
So "real world" complex systems avoid inheritance like the plague. So remind me again why it's desirable?David
As I proposed in an earlier email, one brainstorm I have about this is:
(get-protocol-implementation <type> <protocol-or-interface>) gives you back a mapping of the functions used by that type to implement that protocol.
The only two protocols that involve specifying more than 2 fns is IWatchable (3) and MultiFn (5). It's not clear to me that they would benefit from partial specification.
On Mon, May 21, 2012 at 6:08 PM, Mark EngelbergWe also have macros.
<mark.en...@gmail.com> wrote:
> I never said I want inheritance. I want convenient reuse of partial
> implementations. Inheritance is one way to achieve it, but I'd like to
> think that Clojure can find a better way.
You can always make your own
(defmytype TypeName ...)
and make compile to a deftype that rolls in the protocol
implementations you want to reuse.
So from what I read the philosophy of Clojure discourages inheritance
on concrete data types. However, maybe I am too entrenched in my OO
thinking, how do I define a new record type that includes all the data
members of another record type? I am thinking about the classic
Employee/Person example.
If I can define a record of Employee with Person's data members
included, even that is not true inheritance (as no protocols of
"Person" will be automatically extended to "Employee"), I need that
for function re-use (so a function working on Person will
automatically work on Employee because Employee is guaranteed to have
all the data members in Person).
Also, again, maybe I am too OO minded, is there a way inherit another
record type's protocol implementation? That seems to give the best
combination of both worlds (OO and functional), so you can either have
you own very customized combination of data type/protocols, or use the
very common OO pattern. Just like we have both the single-typed
dispatching (which is more OO like and covers a large portion of use
cases), and more advanced multimethods.
Thanks.
On Mon, May 21, 2012 at 4:00 PM, David Nolen <dnolen...@gmail.com> wrote:
The only two protocols that involve specifying more than 2 fns is IWatchable (3) and MultiFn (5). It's not clear to me that they would benefit from partial specification.
I don't think there's enough body of code using protocols to really know what is going to turn out to be the natural size for protocols but even if we talk about "total" reuse instead of "partial" reuse, the issue is still the same -- the implementation of protocols is generally locked up with no easy way to reuse. If I know I want to implement ILookup the same way it was implemented for some other data structure, I have to be able to see your code, and then copy and paste it. For some things, it is obvious how to reimplement, but for some it is not.
When I implemented priority maps, I had a difficult time figuring out all the interfaces that needed to be implemented, and it was especially difficult to figure out the right way to implement the various notions of equivalence and equality. Basically, I just wanted it to behave like the built-in maps. I had to copy and paste, and shortly after I completed my implementation, a change was made to how these concepts were handled in the core. My notion of equality became out-of-sync with that of built-in maps because I had no convenient way to say, "Do it the way regular maps do it." I ended up having to rewrite that code.
So there is a maintenance issue here too. When Nicolas pointed out the many repetitions of something like the implementation of, say, ILookup, your response was that it would never be a big deal to just do a search-replace if you wanted to make a change. But that assumes that the only copies occur in your own code. When map equality changed, no one automatically did a search and replace in my code to make sure it would stay consistent. This is why code reuse mechanisms that don't rely on copy/paste search/replace are valuable, especially in the core, where other people are trying hard to make structures that behave in a core-like way.
On May 22, 2012 7:09 AM, "Softaddicts" <lprefo...@softaddicts.ca> wrote:
> A better way would be something like:
>
> (defprotocol Personable
> (person [this])
> (age [this] )
>
> (defprotocol CensusOperators
> (age [this]))
>
> (extend-protocol Personable
> Person
> (person [this] this)
> Employee
> (person [this] (:person this)))
>
> (extend-protocol CensusOperators
> Person
> (age [this] (date-dif (java.util.Date.) (:birthday this)))))
>
> Much more convenient to me. I can take anything and make it a person.
I really don't understand your example. Personable#age conflicts with CensusOperators#age if you define them in the same Namespace (and you'll get a warning telling you so) . If they're in separate namespaces , what's the point of Personable #age, given you never define an implementation?
> clojure+unsubscribe@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
Softaddicts<lprefontaine@softaddicts.ca> sent by ibisMail from my ipad!
I think the whole thread is about the trade-off: some people complains
that deftype lacks features.
Another problem with extend is that you can't access your instance
variables with it.
> The only exception is mutable fields in the type where you don't want uncontrolled access. There you indeed need a protocol to handle the synchronisation of the field access.
>
>
And then you need to include everything accessing the internal state of your data structures in a big deftype with little possibility of reuse.
For example, the earlier hash example cannot be put as an extension.
Another related problem is the fact that lambda do not close over the mutable fields within a deftype. (Or at least that is what i understood from several strange error messages.) FP stops at the doors of deftype...
And then you need to include everything accessing the internal state of your data structures in a big deftype with little possibility of reuse.
For example, the earlier hash example cannot be put as an extension.
>
> Access internal state?
>>
Like __hash in your code.
>> For example, the earlier hash example cannot be put as an extension.
>
> Why should most extensions manipulate mutable fields?
>
Most certainly won't. Now none of them can.
> Why should most extensions manipulate mutable fields?Most certainly won't. Now none of them can.
>