This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Builders and builder factories

5 replies
DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.

The current design of Builders as things which have a += method
mirrors quite closely my initial design when I was playing around with
alternative collections API designs a while back. This is unfortunate
as, after a discussion with Jamie Webb who suggested the basic idea, I
replaced it with what I thought was a much better one. :-)

Currently we have Builders and BuilderFactories. A BuilderFactory
creates a Builder, which you then use to build.

What I had instead was

trait Buildable[A, B]{
def build(elements : Traversable[A]) : B;
}

This has a number of advantages over the += approach:

- Reduces concept proliferation. You don't have distinct Builder and
BuilderFactories, they're unified into a single type.
- Means you don't have to expose mutability to the end user
- Improves performance in many cases! Particularly in the case of
mutable collections. e.g. suppose I'm building an array and I'm
building it from something with a known size. Then I can just create
an array of that size and populate it rather than having to resize.
Also because you don't have to be able to reuse Builders in the same
way, given an ArrayBuffer I can just create a new one, append to it
and then return that. etc.

I'm still getting my head around the way BuilderFactory and Builder
are used (in particular the case where some things can return
NoBuilder may be problematic), but I think this would be a significant
improvement to the API design.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Builders and builder factories

Hi David,

Your mail landed in my inbox at the worst possible time, at the
biginning of a two week trip where I could not keep up with mail. So I
am reading this only today.

On Mon, Jun 1, 2009 at 2:17 PM, David MacIver wrote:
> The current design of Builders as things which have a += method
> mirrors quite closely my initial design when I was playing around with
> alternative collections API designs a while back. This is unfortunate
> as, after a discussion with Jamie Webb who suggested the basic idea, I
> replaced it with what I thought was a much better one. :-)
>
> Currently we have Builders and BuilderFactories. A BuilderFactory
> creates a Builder, which you then use to build.
>
> What I had instead was
>
> trait Buildable[A, B]{
>   def build(elements : Traversable[A]) : B;
> }
>
I have thought about it some, and think you are probably right.
However, Buildable can only be a replacement for Builder not
BuilderFactory. BuilderFactory takes a parameter indicating the
current collection. That's necessary for doing the right thing
dynamically.

But for the rest, I think Buildable is promising. I believe it might
also generalize to Views.
So don't be scared of the NoBuilder logic used there, that was just a
hack to indicate that views override the normal build schema. It seems
with Buildable instead of Builder we can have a uniform approach for
both views and strict collections. That would be a win, as would the
performance gains from being able to inspect the source collections.

So I think we should try this. Do you want to give it a go? If things
work out well, I'd be in favor of retaining the new scheme. I won't
have the time to do such experiments soon, that's why I would like to
encourage you to go ahead with this.

Cheers

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Builders and builder factories

2009/6/12 martin odersky :
> Hi David,
>
> Your mail landed in my inbox at the worst possible time, at the
> biginning of a two week trip where I could not keep up with mail. So I
> am reading this only today.

No problem. I've mostly lacked sufficient time to pursue it too!

> On Mon, Jun 1, 2009 at 2:17 PM, David MacIver wrote:
>> The current design of Builders as things which have a += method
>> mirrors quite closely my initial design when I was playing around with
>> alternative collections API designs a while back. This is unfortunate
>> as, after a discussion with Jamie Webb who suggested the basic idea, I
>> replaced it with what I thought was a much better one. :-)
>>
>> Currently we have Builders and BuilderFactories. A BuilderFactory
>> creates a Builder, which you then use to build.
>>
>> What I had instead was
>>
>> trait Buildable[A, B]{
>>   def build(elements : Traversable[A]) : B;
>> }
>>
> I have thought about it some, and think you are probably right.
> However, Buildable can only be a replacement for Builder not
> BuilderFactory. BuilderFactory takes a parameter indicating the
> current collection. That's necessary for doing the right thing
> dynamically.

I think I've figured out how to make this work with just the one class
actually. All we need to do is move the extra type parameter onto
Buildable, so it becomes

trait Buildable[V, R]{
def build(view : View[V, T]) : R;
}

Then e.g. map becomes

def map[S, R](f : T => S)(implicit buildable : Buildable[This, S, R])
: R = buildable.build(this.view.map(f));

I think this should largely work. I'll need to experiment to figure it out.

The big downside of taking this approach over the version without the
parameter for the original collection is that it axes one of my
favourite little features of alt.collections (which I'd meant to bring
up but forgot), which was the ability to do

myCollection.as[SomeCollectionType]

on arbitrary collections and have this work wherever it compiled. So I
might want to investigate some other approach.

> But for the rest, I think Buildable is promising. I believe it might
> also generalize to Views.
>
> So don't be scared of the NoBuilder logic used there, that was just a
> hack to indicate that views override the normal build schema. It seems
> with Buildable instead of Builder we can have a uniform approach for
> both views and strict collections. That would be a win, as would the
> performance gains from being able to inspect the source collections.

Yes, one of the perks of Buildable is that it works seamlessly for
lazy collections.

> So I think we should try this. Do you want to give it a go? If things
> work out well, I'd be in favor of retaining the new scheme. I won't
> have the time to do such experiments soon, that's why I would like to
> encourage you to go ahead with this.

Sure. I'll see what I can put together over the next couple of days.

David

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Builders and builder factories

On Fri, Jun 12, 2009 at 11:14 PM, David MacIver wrote:
> 2009/6/12 martin odersky :
>> Hi David,
>>
>> Your mail landed in my inbox at the worst possible time, at the
>> biginning of a two week trip where I could not keep up with mail. So I
>> am reading this only today.
>
> No problem. I've mostly lacked sufficient time to pursue it too!
>
>> On Mon, Jun 1, 2009 at 2:17 PM, David MacIver wrote:
>>> The current design of Builders as things which have a += method
>>> mirrors quite closely my initial design when I was playing around with
>>> alternative collections API designs a while back. This is unfortunate
>>> as, after a discussion with Jamie Webb who suggested the basic idea, I
>>> replaced it with what I thought was a much better one. :-)
>>>
>>> Currently we have Builders and BuilderFactories. A BuilderFactory
>>> creates a Builder, which you then use to build.
>>>
>>> What I had instead was
>>>
>>> trait Buildable[A, B]{
>>>   def build(elements : Traversable[A]) : B;
>>> }
>>>
>> I have thought about it some, and think you are probably right.
>> However, Buildable can only be a replacement for Builder not
>> BuilderFactory. BuilderFactory takes a parameter indicating the
>> current collection. That's necessary for doing the right thing
>> dynamically.
>
> I think I've figured out how to make this work with just the one class
> actually. All we need to do is move the extra type parameter onto
> Buildable, so it becomes
>
> trait Buildable[V, R]{
>   def build(view : View[V, T]) : R;
> }
>
it seems you forgot the third type parameter here! But we also need a
value parameter
indicating the current collection. That's needed so that the following
will work:

scala> val x: Traversable[Int] = ArrayBuffer(1, 2, 3)
x: Traversable[Int] = ArrayBuffer(1, 2, 3)

scala> x map (1 +)
res3: Traversable[Int] = ArrayBuffer(2, 3, 4)

Cheers

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Builders and builder factories

2009/6/13 martin odersky :
>> I think I've figured out how to make this work with just the one class
>> actually. All we need to do is move the extra type parameter onto
>> Buildable, so it becomes
>>
>> trait Buildable[V, R]{
>>   def build(view : View[V, T]) : R;
>> }
>>
> it seems you forgot the third type parameter here! But we also need a
> value parameter
> indicating the current collection. That's needed so that the following
> will work:
>
> scala> val x: Traversable[Int] = ArrayBuffer(1, 2, 3)
> x: Traversable[Int] = ArrayBuffer(1, 2, 3)
>
> scala> x map (1 +)
> res3: Traversable[Int] = ArrayBuffer(2, 3, 4)

Right. But that's what V on the View is isn't it?

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Builders and builder factories

On Sat, Jun 13, 2009 at 5:09 PM, David MacIver wrote:
> 2009/6/13 martin odersky :
>>> I think I've figured out how to make this work with just the one class
>>> actually. All we need to do is move the extra type parameter onto
>>> Buildable, so it becomes
>>>
>>> trait Buildable[V, R]{
>>>   def build(view : View[V, T]) : R;
>>> }
>>>
>> it seems you forgot the third type parameter here! But we also need a
>> value parameter
>> indicating the current collection. That's needed so that the following
>> will work:
>>
>> scala> val x: Traversable[Int] = ArrayBuffer(1, 2, 3)
>> x: Traversable[Int] = ArrayBuffer(1, 2, 3)
>>
>> scala> x map (1 +)
>> res3: Traversable[Int] = ArrayBuffer(2, 3, 4)
>
> Right. But that's what V on the View is isn't it?
>
Ah, I had overlooked that. -- Martin

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland