dfdx icon indicating copy to clipboard operation
dfdx copied to clipboard

Builder-style type constructors

Open opfromthestart opened this issue 2 years ago • 9 comments

I don't remember where I saw it but I saw an interesting way of making constructors for objects like

let x = Type::new().a(5).b(4);

instead of

let x = Type::new(5,4);

since it allows for adding parameters to a type without breaking an api, as well as being easier to read without relying on an ide. (Having functional constructors may be nice to have for ordinary

I was thinking that this would be useful with some complicated types (like Conv2D) to allow them to have minimal specification So instead of Conv2D<64, 128, 3, 1, 2> it could be something like Conv2D<64, 128>::K<3>::S<1>::P<2>. This would allow only necessary values (eg Conv2D<64, 128>::K<3>::P<2> would infer a stride of 1) to be entered and allows values to be entered out of order. I am not entirely sure that this can be done without requiring using traits (which would just make it look worse) but it may make certain types better to create.

It may be able to be done using associated constants and having builders be constants instead of types. This would obviously break everything else but it should allow arbitrary additions with no breaking changes.

opfromthestart avatar Apr 11 '23 20:04 opfromthestart

Ahh yes this is the builder pattern. I really like the idea of doing it at type level, might have to play around with that to see how it's possible. Good idea!

chelsea0x3b avatar Apr 12 '23 13:04 chelsea0x3b

I wonder if this is actually possible, because when try to do Type::Associated1<X>::Associated2<Y>, rust usually makes me do <Type::Associated1<X> as Trait>::Associated2<Y> to disambiguate

nkoppel avatar Apr 12 '23 15:04 nkoppel

I was thinking that something like

struct Thing<const A: usize = 0, const B: usize = 0>;
impl<const A_: usize, const B_: usize> Thing<A_, B_> {
    const A::<const A2: usize> : Thing<A2, B_> = Thing::<A2, B_>;
    const B::<const B2: usize> : Thing<A_, B2> = Thing::<A_, B2>;
}

would work but you cant put generics on constants I guess. But it would work without needing traits, which was what I wanted to avoid. I was looking at https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants in the rust book

opfromthestart avatar Apr 13 '23 01:04 opfromthestart

You could have several intermediate types:

struct TypeA;
impl TypeA {
    fn a<const A: usize>() -> TypeB<A> { TypeB }
}
struct TypeB<const A: usize>;
impl<const A: usize> TypeB<A> {
    fn b<const B: usize>() -> TypeC<A, B> { TypeC }
}
struct TypeC<const A: usize, const B: usize>;
// etc.

ViliamVadocz avatar Apr 15 '23 00:04 ViliamVadocz

That still isn't very satisfying since it either still requires having all arguments in order, or requires O(n^2) methods between all of the structs.

opfromthestart avatar Apr 15 '23 01:04 opfromthestart

I could see something like that working if the current model system is completely overwritten, and instead of builders being types they are objects, but I don't know if that would work either.

opfromthestart avatar Apr 15 '23 01:04 opfromthestart

Might be possible with rewrite in #854

chelsea0x3b avatar Aug 18 '23 20:08 chelsea0x3b

It would be some, but my idea for this was to allow builder pattern even for const layers, so you can use the builder pattern on types at compile time instead of at runtime.

opfromthestart avatar Aug 24 '23 13:08 opfromthestart

Somewhat related: It can be useful to see what generic parameters our generic arguments refer to, and there is a related issue on rust-analyzer: https://github.com/rust-lang/rust-analyzer/issues/11091

swfsql avatar Nov 17 '23 03:11 swfsql