G'day. On Mon, Feb 17, 2003 at 01:44:07AM -0500, Mike T. Machenry wrote:
> I was wondering if it's better to define them as type classes with the > operations defined in the class. What do haskellian's do? I can't speak for other Haskellians, but on the whole, it depends. Here's the common situation: You have a family of N abstractions. (In your case, N=2.) The abstractions are similar in some ways and different in some ways. The most appropriate design depends largely on what those similarities and differences are. >From your definitions, it seems clear that there is some common structure. That suggests that a vanilla type class solution may not be appropriate, because type classes do not directly support common structure, only related operations. Your solution wasn't bad: > data PlayerState = > FugitiveState { > tickets :: Array Ticket Int, > fHistory :: [Ticket] } | > DetectiveState { > tickets :: Array Ticket Int, > dHistory :: [Stop] } If the operations on the "tickets" field are different, or the algorithms which operate on PlayerState are different for the FugitiveState case and the DetectiveState case, this may be a good design, because by not sharing structure, you're explicitly denying any similarity (i.e. just because look the similar, that doesn't mean they are similar). If they are similar, then this may not be an appropriate design. Ideally, you want to use language features and/or idioms which expose the similarities (where they exist) and the differences (where they exist). Here's one design where the structural similarity is explicit: data PlayerState = PlayerState { tickets :: Array Ticket Int, role :: RoleSpecificState } data RoleSpecificState = FugitiveState { fHistory :: [Ticket] } | DetectiveState { dHistory :: [Stop] } Depending on how similar the operations on the RoleSpecificState are (say, if they are related by a common type signature, but have little code in common), or if you want a design which is extensible to other kinds of player (possibly at dynamic-link time) you may prefer to use type classes to implement the role-specific states instead: -- Warning: untested code follows class RoleSpecificState a where {- ... -} data FugitiveState = FugitiveState { fHistory :: [Ticket] } instance RoleSpecificState FugitiveState where {- ... -} data DetectiveState = DetectiveState { fHistory :: [Ticket] } instance RoleSpecificState DetectiveState where {- ... -} data PlayerState = forall role. (RoleSpecificState role) => PlayerState { tickets :: Array Ticket Int, role :: role } (If you know anything about design patterns, you may recognise this as being similar to the "Strategy" pattern. This is no accident.) It's hard to say what is the most appropriate design without looking at the algorithms and operations which act on the PlayerState type and analysing their similarities and differences. > Oh and if I say > Instance Foo Baz where > ... > > and only define a few of the operations in Foo... bdoes Baz take on some > default methods? If you've declared default methods, yes. Cheers, Andrew Bromage _______________________________________________ Haskell mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell