My strategy here would be different, the conversion fn would be part of
the cell attributes. Maybe with the help of a lightweight factory fn to ease
record allocation.

The Convertable protocol would simply refer to the closure attribute, call it
and pass this as the unique parameter.

The reasons for this choice ?

A) The delta between the derived types is minimal, in your example,
      it's not the data that changes, only a variation in the rendering.

B) Rendering is a fundamental operation.
     Without it, I think the Cell would be much less useful if not at all.
     It's pretty much part of the Cell.

C) Locality.

     In real life you would probably have more than one operation that needs a
     dispatch. If you create new types and protocols to cope with this, you
     will end up with a maze of definitions. Using a helper factory fn to 
allocate
     the record and choosing appropriate dispatchs will
     centralize this in one spot.

     No heavy cond needed at rendering time, just at dispatch assignment.
     The dispatch can then refer to private fns that will take care of rendering
     far away from your data type/protocol definitions.

     Take your example and add 30 variations of rendering as defrecords and
     defprotocol and let me know how your esthetic sensor feels afterward :)

     I am not fond of creating variations of defrecords when the data 
attributes are
     the same. Already in Java I cannot stand the russian puppet syndrom where
     ancestor classes have a couple of getters/setters less than their 
descendants :)

Of course if the data properties varies a lot and you end passing nil to many
constructor args, then you have a clear case where it's time to
create different types and corresponding dispatch/protocols. Up to you to
figure when and why...

I try to restrict my use protocols to bridge behavior between different Clojure 
data types
or to add different behaviors to these, so far so good. My needs for 
defrecord/deftype
is nearly nil.

I do not try to hide implementation details while operating basically on the 
same data
representation by adding more definitions.
Other strategies exist that require less code and definitions, dispatch fns is 
one,
multi methods another....

Luc P.

> Hey Luc,
> 
> That's a cool casting strategy to cleanly build a standard way to get at 
> the person piece of any type of applicable record.  In the pure composition 
> case, it's actually a nice solution to build functions know how to unpack 
> the Person aspect of a subtype.
> 
> However, I think which strategy is best depends very much on whether you 
> plan to extensively change the protocol implementation between the the 
> different  related types.  In the case where Employee simply carries extra 
> information with regard to a person, I agree that it is nice simply to 
> define a mechanism to extract the base type and just act on it directly.  
> However if you wish to do something more complicated or override default 
> behavior extensively, I think the encapsulated "newtyped" way is more 
> convenient.
> 
> For example, how about we build a little DSL to represent different types 
> of Excel cells.  In Excel, everything is represented as a float, so we have 
> to keep track of the type separately.  I think using a record type is a 
> fairly natural way to encode this extra data on type of Cell, where I might 
> have reached for inheritance in traditional OOP.
> 
> (defprotocol Convertable
>   (convert-cell [this]))
> 
> (defrecord Cell [row col float-value])
> 
> (defrecord MoneyCell [cell])
> 
> (defrecord DateCell [cell])
> 
> (defrecord NumberCell [cell])
> 
> (extend-protocol Convertable
>     NumberCell
>     (convert-cell [this] (float (-> this :cell :float-value))
>     DateCell
>     (convert-cell [this] (excel-date (-> this :cell :float-value)) ;; 
> Translate an Excel datestamp to java.util.Date
>     MoneyCell
>     (convert-cell [this] (bigdec (convert-cell (Number (:cell this))))) 
> ;;BigDecimals are for exact money handling.
> 
> Blindly unpacking the Cell type (via a Cellable convention like in the 
> Person example) would lose information we need to accurately describe the 
> conversion process and call the best function.  This strategy also 
> leverages ones of the best aspects of protocols: how extensible they are to 
> new types of data.  If I ever decide to support a StringCell type or a 
> BooleanCell, I would just be one extend-protocol away rather than having to 
> rewrite some gnarly embedded cond to work with my new type of cell.  Of 
> course for operations that don't care about their type, creating a protocol 
> to extract the cell and working directly with its attributes (such as 
> manipulating row and position directly), makes a great deal of sense.
> 
> Best,
> Mark
> 
> On Monday, May 21, 2012 10:09:02 PM UTC-7, Luc wrote:
> >
> > Interesting demonstration, except for one thing, defining getters is a 
> > waste of 
> > time :) 
> >
> > When you need to define accessors, you start to run into inefficient use 
> > of 
> > your time. It still a bearable workload in Java because of all this heavy 
> > tooling that allows 
> > to select for which fields accessors will be generated. 
> > Without getters, you would also expose mutable stuff to the universe. 
> >
> > However in Clojure the values are not mutable... Why bother defining 
> > an accessor to a read-only value ? 
> >
> > 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. 
> > Even from a Clojure map just by extending it and returning a Person 
> > instance. 
> > You expose the Person abstraction when available instead of hiding it. 
> >
> > Computed values like the age ? This is something that could be computed 
> > for other entities like an animal. 
> >
> > So you can write (age (person xxxx)) or (age (animal xxxx)). 
> >
> > This is why I tell people to get out of their usual thinking process. Get 
> > lazy, 
> > playing hide and seek was fun at pre-school age but in your code and data 
> > structures ? 
> >
> > Luc P. 
> >
> >
> > > I think it's misleading to use inheritance to reduce code duplication.   
> > > Inheritance is about indicating function typing and creating typed 
> > > contracts, both of which are fairly un-idiomatic in Clojure. 
> > > 
> > > However, there is another way to prevent code duplication: use 
> > composition 
> > > instead.  Instead of having the Employee class mirror the attributes of 
> > the 
> > > Person (which if you really wanted, you could easily do via a 
> > "defperson" 
> > > like macro that would slug on extra fields), why not have an Employee 
> > carry 
> > > a Person as one of its attributes?  This approach is more similar to the 
> > > Haskell/functional way of working with record types (I think Haskell's 
> > > newtype operator works similarly under the covers).  There is also an 
> > > analogue to the decorator pattern in Java. 
> > > 
> > > In this case, you would specify a Personable protocol, which both Person 
> > > and Employee implement; for all the basic operations, Employee would 
> > just 
> > > defer to its internal person.  Example: 
> > > 
> > > (defrecord Person [name birthday]) 
> > > 
> > > (defrecord Employee [title person]) 
> > > 
> > > (defprotocol Personable 
> > >   (name [this]) 
> > >   (age [this)) 
> > > 
> > > (extend-protocol Personable 
> > >   Person 
> > >   (name [this] (:name this)) 
> > >   (age [this] (date-dif (java.util.Date.) (:birthday this)) ;;date diff 
> > > defined elsewhere 
> > >   Employee 
> > >   (name [this] (name (:person this)) 
> > >   (age [this] (age (:person this))) 
> > > 
> > > Arguably, if we were deferring to the current Java best practices and 
> > > encapsulation, one should be creating interfaces of getters and setters 
> > > rather than directly accessing instance variables anyway, making the 
> > extra 
> > > protocol definition no more work than doing it correctly in modern Java. 
> > > 
> > > Best, 
> > > Mark 
> > > 
> > > On Sunday, May 20, 2012 10:22:55 AM UTC-7, Warren Lynn wrote: 
> > > > 
> > > > 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. 
> > > 
> > > -- 
> > > You received this message because you are subscribed to the Google 
> > > Groups "Clojure" group. 
> > > To post to this group, send email to clojure@googlegroups.com 
> > > Note that posts from new members are moderated - please be patient with 
> > your first post. 
> > > To unsubscribe from this group, send email to 
> > > clojure+unsubscr...@googlegroups.com 
> > > For more options, visit this group at 
> > > http://groups.google.com/group/clojure?hl=en 
> > -- 
> > Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my ipad! 
> >
> 
> -- 
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your 
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
Softaddicts<lprefonta...@softaddicts.ca> sent by ibisMail from my ipad!

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to