#Unsure how to do my API

24 messages · Page 1 of 1 (latest)

tender harness
#

I want to have basic drop-in components, I'm making a sort of incredibly simplified LLVM clone that works equally well as a compiler and as a decompiler and I want to take in something that will generate my IR from bytecode

My first thought was to take in a function that returned what I wanted, then I thought for a bit and got the idea to make a trait which forced you to have a serialize and deserialize function, but now I'm thinking I don't like the idea of making consumers make a class and that I want to make it use functions again

I'm stuck, any opinions and insight would be nice, would you rather make a class or would you rather pass in a function?

#

I think this is inversion of control

gritty pewter
#

I absolutely despise design patterns so I don’t know if it’s inversion of control or not, but one thing to keep in mind is that “a function”, is, in and of itself, a trait

#

because you’re ultimately just accepting “something that implements FnMut” or something

#

So really the question here to be asking is: Should I use an ad-hoc trait with a more limited signature or a more general trait allowing for more abilities?

#

Now, as you develop the code the answer should hopefully become obvious

#

If your trait just has one method and most of the time you’re just using some kind of make_the_trait_from_a_function adapter, then it should probably be a function

#

If it has multiple methods, conceivably has utility methods based on them and things that compose them, then a full-fledged trait is probably worth it

#

It’s the same distinction between structs and tuples, really — when do you use struct Foo { x: u32, y: String } versus (u32, String)?

#

Another thing to keep in mind is that this is not the most generic interface. If you wanted the most generic interface, it’s best to have a state machine-like design

#

In that design, you don’t accept a trait or a callback — rather, you simply hand over control flow (i.e. return) to the caller when you would have called the callback

#

This is the most flexible thing possible since the user is free to do whatever they want in the “trait implementation” which is now just caller code:

  • break from a loop
  • await on something
  • throw an error
  • etc
#

It can often be more difficult to implement, however, and leads to more complex APIs

#

but is definitely the most elegant solution in cases (e.g. iterators do this and it’s Good)

tender harness
#

huh

#

yea i guess

#

maybe i could use control flow but it probably doesn't make sense

tender harness
#

i think ill use Fn

#

its a bit more restricting on consumers of the crate but its cleaner, since the trait only wants 2 methods

#

actually after some thought no ill use the trait because Fn would be too restricting

#

with the trait consumers of the crate can make as big a struct that they want with all the functionality they want for their language/bytecode format

#

jvm bytecode will differ from x86 which will differ from some random arm chip

#

but that doesnt make sense because serialisation and deserialisation should have no options

#

ill just use fn, it will become clear if this was a good decision later