Extending Base functions for a tree

As I recall,

  • Base.length is used to return types with a single dimension
  • Base.size is used to return types with more than one dimension

I have build a tree type and I want to return the total number of nodes in the tree. Should I extend length, size or neither?

Further, each node of the tree carry a “weight”. So I am thinking about extending Base.sum for the total weight.

Any thoughts welcome. Thanks!

size should probably be consistent with length if implemented. Assuming the type support iteration implementing length will be very fair.

For sum, again, if it is iterable it’ll be strange if the sum is not summing the item in iteration. If iteration returns the weight it’ll be fair but otherwise I think you’ll be better off with a different function

2 Likes

When you say consistent, do you mean that size should return a tuple and length should return a single number? I was struggling due to the shape of the tree because it is neither linear nor multi-dimensional :slight_smile:

The type supports iteration in the sense that it can be traversed depth-first or breath-first (currently depth-first only). The nodes have composite type so I am implementing sum(f, itr) interface and expects function f to return a numeric type.

Yes. And although I’m not sure right now what the official rule would be, my rule of thumb will be that if you support indexing then it has an interface that looks like an array that you can have size. If you don’t then don’t bother.

Put it another way, follow Set or Dict.

In that case I’d say if the iteration returns nodes that doesn’t support arithmetic then you probably should still not special case sum even though it does’t really conflict with anything. (But sincne there’s no conflict you can also do what you feel convinient without causing much trouble). sth like sum(node_weight, iter) shouldn’t be too hard to write either though…

1 Like

Actually, there could be a couple of summable fields besides weight. Should I define something like this?

Base.sum(::Tree) = error("Please use `sum(f, ::Tree)` instead.")

Mmm… if I do that then it seems to be a hassle because I would have to do the same thing for other statistical functions like mean, std, var, etc.

Just leave them unimplemented should be fine? You are not extending all the functions that you are not planning to implement are you?

Sure, I can leave them unimplemented. I was just thinking about being a good “citizen” for implementing the full interface. Thanks for your help.

This depends on whether you want to support any of the interfaces.

I am not sure that the array interface, which has size, makes much sense for a tree. But if you want to support iteration, you should define Base.length and the relevant traits if applicable. And, crucially, Base.iterate.

If you do not want to support either interface, you are free to do what you like. Some types in packages define length without supporting iteration.