Skip to content
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

Rename the parameters of Range.exponentialFrom (and friends) #313

Closed
dharmaturtle opened this issue Feb 8, 2021 · 5 comments · Fixed by #315
Closed

Rename the parameters of Range.exponentialFrom (and friends) #313

dharmaturtle opened this issue Feb 8, 2021 · 5 comments · Fixed by #315
Milestone

Comments

@dharmaturtle
Copy link
Member

/// Construct a range which scales the bounds exponentially relative to the
/// size parameter.
let inline exponentialFrom (z : 'a) (x : 'a) (y : 'a) : Range<'a> =

z, x, and y are pretty opaque. How do we feel about renaming these to origin, lowerBound, and upperBound respectively?

I intend the rename to also affect exponentialBounded, linear*, and constant*.

Haskell uses z, x, and y, but it looks like they have docs on each specific parameter... which F# can't do yet 🙄

Thoughts?

@TysonMN
Copy link
Member

TysonMN commented Feb 8, 2021

I think this is an improvement

@moodmosaic
Copy link
Member

I thought z, x, and y are great variable names in this particular case? 🤔

@TysonMN
Copy link
Member

TysonMN commented Feb 8, 2021

I don't think Mark's argument applies here.

Here is his first point including what I think is an implicit assumption.

Functions are sometimes so generic that we can't say anything more meaningful about such values [that is not already said by the types of the values].

These three values all have the same type, but they play different roles. I think the following is ambiguous given only the API.

Suppose I want to represent an exponential range (of years) from 1900 to 2100 that shrinks towards 2000. How should I write this?

  • exponentialFrom 1900 2100 2000 because that order matches the order that seems natural to me when expressing my desire in English?
  • exponentialFrom 1900 2000 2100 because that puts the arguments in order from smallest to largest?
  • exponentialFrom 2000 1900 2100 because... what is the best argument here? Not sure. Maybe the origin is typically known before the two bounds, so it could be partially applied?

I only know the correct order because I looked at the implementation. The correct order is the last one. The names suggested by @dharmaturtle would remove this ambiguity.

For an example, consider this line I added in PR #239.

let addChild (child: Tree<'a>) (parent: Tree<'a>) : Tree<'a> =
    let (Node (x, xs)) = parent
    Node (x, Seq.cons child xs)

Since the both arguments have the same type, I think it is important to use the identifiers to distinguish how they are used.

For another example, consider List.append, which has type 'a list -> 'a list -> 'a list and uses identifiers list1 and list2. Those names are sufficiently descriptive because the only thing that matters is in this case is the order in which the lists are appended and that order is determined by the order in which the arguments are supplied.

@moodmosaic
Copy link
Member

I agree with your point of view. I believe that this point of view is basically also the point of view of most of the users out there (that don't know―or don't want to know―the history and/or the implementation behind all this).

It can be seen as an improvement, then, origin, lowerBound, and upperBound. (Of course the type system won't stop you from messing around with the order, but that's a different discussion.)

👍

@TysonMN
Copy link
Member

TysonMN commented Feb 8, 2021

I agree with Mark that single-letting names are often perfectly clear. I am just not convinced that this is one of them.

Maybe one way to improve the situation is introducing another type. Maybe call it Interval<'a>. Then a Range<'a> could be an origin and an Interval<'a>. Naming is impossibly hard, so these are probably not the right names. The goal is to makes the types different.

The issue of functions with arguments of the same type is interesting. It is often said that functions with cyclomatic complexity 1 don't need to be tested. However, this includes functions with calls like List.append b a that might be wrong because it should be List.append a b instead. This wisdom here is that this code would wrong for every execution, so essentially all executions work or none will, but the counterargument is that List.append b a = List.append a b when either a = [] or b = [], so there is still some nontrivial complexity there.

This issue was closed.
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 a pull request may close this issue.

3 participants