Up to Main Index Up to Journal for December, 2014 JOURNAL FOR MONDAY 22ND DECEMBER, 2014 ______________________________________________________________________________ SUBJECT: Complicating the simple DATE: Mon 22 Dec 14:55:10 GMT 2014 NOTE: Due to one thing and another this post wasn't uploaded until Wednesday 24th December even though it was written on Monday 22nd December :( I've been spending a lot of time recently on WolfMUD-mini[1] with the intent of using it to improve the main WolfMUD code. As you may remember from last time things were going quite well. I could walk around examining things, get and drop items and containers were working. Since then I added a readable type. This is where things got a little sticky. Initially I created a simple readable - a plaque - that could be read. Then I thought to myself that this should be usable with other things and wanted to put some writing onto the mug used in the previous example. Originally the mug was defined as: MUG is a THING is an ITEM is an INVENTORY is a CONTAINER type container struct { *inventory *item } For a readable container this becomes: MUG is a THING is an ITEM is an INVENTORY is a CONTAINER is READABLE type container struct { *inventory *item *readable } Things were getting ugly fast :( Before I had had things like containers and readable containers and with Go's embedding this became very easy and I could do things like 'if r.(is.Readable) {}'. However I was ending up with things like: type container struct { *inventory *item *readable } func (c *container) Get{} func (c *container) Drop{} func (c *container) Put{} func (c *container) Examine{} func (c *container) Read{} type Readable interface { Read() } type Examinable interface { Examinable() } type Gettable interface { Get() } type Dropable interface { Drop() } I was just tagging types with interfaces and the logic was going into the parser as something like: if x, ok := x.(is.Readable); ok { ... } if x, ok := x.(is.Examinable); ok { ... } if x, ok := x.(is.Gettable); ok { ... } if x, ok := x.(is.Dropable); ok { ... } This felt very ugly. Also I should be able to make a container - or anything else - readable without having to create a new type for it. It was at this point that I hit another issue. If you have an embedded readable type you want to display something like: You read a plaque. It says: Please do not read the plaques! However the reading is done by the readable type: func (r *readable) Read() { ... } When using an interface value as a receiver the type passed is the type of the receiver. In other words you are just passing the embedded readable and nothing else. In this case we cannot display 'You read a plaque' because 'a plaque' comes from calling Name() on the embedded *thing type - which we now don't have a reference to because we just have the readable type :( Embedding references to all the other embedded types in each type seems very messy and very wrong - so what to do? *sigh* An idea I am playing with now is to have 'things' made up of a slice of behaviours. The only requirement of a behaviour would be to have a SetParent method. This would be a pointer to the whole slice itself allowing all behaviours to access each other for a 'thing'. One immediate issue here would be finding the right behaviour - you would need to loop over the slice and do a type assertion on each behaviour to see if it implemented the interface you are looking for, and this would need to be done for each 'thing' you are interested in. So glad I wrote WolfMUD-mini to play with these ideas - a fundamental rewrite can be done in an hour or so to try out these different approaches :) -- Diddymus [1] See 11.html Up to Main Index Up to Journal for December, 2014