Skip to content

Refactor threshold method #412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2022

Conversation

tcharding
Copy link
Member

Do a few refactorings in an attempt to make the threshold method easier to read.

tcharding added 3 commits May 18, 2022 08:47
The `threshold` function is a candidate for refactoring because it
contains code at multiple layers of abstraction as well as duplicate
code.

Refactor out the sorting closures into three, very similar, functions.

Refactor only, no logic changes.
Currently we are using a mutable variable to manually implement the
logic that the `fold` combinator provides.
// costy satisfactions are satisfied, the most costy dissatisfactions are dissatisfied).
//
// Args are of form: (<count_sat>, <count_dissat>)
fn sat_minus_dissat<'r, 's>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cb4c037:

I think all these functions should be defined at the top of the function that they're used in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad you brought this up, I've wanted to discuss it with you for ages now :) When I started writing Rust I had come from C and used to do as you suggest. I was then introduced to the idea of laying out a source file with the most important things at the top, this implies putting functions below where they are called. Another data point; I also read the idea somewhere of writing functions at a single level of abstraction the writing next, and below that, all the functions at the next layer of abstraction and so on.

Related; we have a bunch of places where the error struct and implementation is at the top of the file, I'd prefer to see that code buried way down lower so I don't see it very often.

Is there a reason you favour putting functions above where they were first called (apart from habit from writing/reading C code)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like these functions' visibility to match the only scope where they're used, since they are essentially helpers for the code in that scope.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't about being C-like -- you can't even do what I'm suggesting in C.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no strong preferences here.
If the name is descriptive and is being used multiple times in different methods(I think it is the case for some of the functions here). It makes sense to have it at the bottom.

On the other hand, if it is just a helper function, it makes sense to have it a function near the body or even a sub-function inside the main function. The readers can cleanly separate these things.

fn opt_add(a: Option<usize>, b: Option<usize>) -> Option<usize> {
a.and_then(|x| b.map(|y| x + y))
}

/// Returns Some((x0+y0, x1+y1)) is both x and y are Some. Otherwise, returns `None`.
fn opt_tuple_add(a: Option<(usize, usize)>, b: Option<(usize, usize)>) -> Option<(usize, usize)> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 28662d2:

Same here. I think this'd be clearer defined close to its use.

Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 28662d2

I imagine this will be a somewhat controversial change :)

@tcharding
Copy link
Member Author

I imagine this will be a somewhat controversial change :)

Do you mean the combinator stuff or the 'sort_by' functions? I'm not 100% sure the sort_by functions are better than inline.

@apoelstra
Copy link
Member

I mean the combinator stuff -- I've gotten pushback in rust-bitcoin for being too functional. (But in this library maybe it's ok..)

@tcharding
Copy link
Member Author

I've gotten pushback in rust-bitcoin for being too functional.

lolz, we should get some less-functional devs around here.

@tcharding
Copy link
Member Author

tcharding commented May 19, 2022

More seriously, this is a valid concern. I get that functional style combinators are hard to understand if one is not familiar with them.

On the other hand, it took me a while to work this code out, when I first read it I couldn't work out why we were setting the variable continuously, then I had to check exactly how the and_then chained with map worked in relation to None and think through the variable mutation loop - when one sees fold its much more obvious whats happening (IMNSHO).

@tcharding
Copy link
Member Author

But in this library maybe it's ok..

I'm yet to work out whats the separation between the various crates in our stack, i.e., different people, different ethos, different coding expectations, different review expectations, different coding styles? I'm defaulting to treating them all the same, is this wrong?

@apoelstra
Copy link
Member

This library is maintained by programming language people (Sanket and me, mainly), so generally you can get away with more obscure CS idioms than you could in rust-bitcoin. (It also, by nature, requires much more CS-heavy code.)

Similarly rust-secp is maintained by cryptographers so you can get away with being more crypto-heavy.

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 28662d2. IMO, these are clean improvements.

I have a very weak preference towards @tcharding's suggestion of hiding the methods away particularly because the names are descriptive.
Reading opt_add, sat_minus_dissat_cost the reader can guess what the functions might do. However, for a generic helper function or something similar. I would side with @apoelstra.

// costy satisfactions are satisfied, the most costy dissatisfactions are dissatisfied).
//
// Args are of form: (<count_sat>, <count_dissat>)
fn sat_minus_dissat<'r, 's>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no strong preferences here.
If the name is descriptive and is being used multiple times in different methods(I think it is the case for some of the functions here). It makes sense to have it at the bottom.

On the other hand, if it is just a helper function, it makes sense to have it a function near the body or even a sub-function inside the main function. The readers can cleanly separate these things.

@sanket1729
Copy link
Member

We can continue this discussion but merging this based on Andrew's ACK.

@sanket1729 sanket1729 merged commit 5649628 into rust-bitcoin:master May 19, 2022
@tcharding
Copy link
Member Author

Every now and again I play with defining functions inside of functions but I'm never that happy with the result, because of all the types Rust has on functions its never that clean. Now I write this, perhaps defining name closures would have been nice in this case. I'll have a play with that.

@apoelstra
Copy link
Member

I wouldn't recommend naming closures -- I agree, that quickly seems to go bad in surprising ways (like, call a closure once and it'll seem to work, then years later add a second call and watch the compiler blow up). But if you define a function inside a function with the fn syntax there's usually no problem.

@tcharding tcharding deleted the 05-18-refactor-threshold branch May 24, 2022 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants