Improve regular_nested example for “Polymorphism” chapter of the manual#13666
Conversation
|
I agree that it is a good idea to simplify the specification of this function, and it is also a good idea to have the function name reflect its type. I have few minor comments on how we could simplify it further that you might want to consider. Thanks for the PR ! |
|
Sure… Regarding Currently in the PR: let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + max_regular_depth n
and max_regular_depth = function
| [] -> 0
| a::q -> max (regular_depth a) (max_regular_depth q);;using let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left (fun acc a -> max acc (regular_depth a)) 0 n;;using let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left max 0 (List.map regular_depth n);;the original code with minimal changes (new name, let rec regular_depth = function
| List _ -> 1
| Nested [] -> 1
| Nested (a::q) -> max (1 + regular_depth a) (regular_depth (Nested q));;Regarding the Arguably, one could even consider to use a base case of single element type 'a regular_nested = Elem of 'a | Nested of 'a regular_nested listtype 'a nested = Elem of 'a | Nested of 'a list nested;;which helps also in that the type 'a nested = BaseCase of 'a | MoreNesting of 'a list nested;;(Etc… Then one could define the But IMO any superficial similarity here helps avoid distracting from the main point of this section, which is not about how to implement a nested list data structure correctly. Also such a change would involve refactoring … well …everything below, which is going way too far. Though I get your arguments, especially We can’t simply define a base case of
And I would really like for something like Perhaps, it’s workable to define it as “maximal number of Reiterating the alternatives, with that specification they’d now look as follows:
let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + max_regular_depth n
and max_regular_depth = function
| [] -> 1
| a::q -> max (regular_depth a) (max_regular_depth q);;
let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left (fun acc a -> max acc (regular_depth a)) 1 n;;let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left max 1 (List.map regular_depth n);;
let rec regular_depth = function
| List _ -> 1
| Nested [] -> 2
| Nested (a::q) -> max (1 + regular_depth a) (regular_depth (Nested q));;So that’s 2*4 versions now, which would you prefer? ;-) |
|
I agree with the argument that we want the two depths function to agree on inputs that are morally equivalent. In this case, I think that your idea to keep Concerning the implementation, I think we want to keep it as simple as possible since like you said the implementation is not the point (we just want it to be coherent to not nerdsnipe readers). Thus I am hesitating between let rec regular_depth = function
| List _ -> 1
| Nested [] -> 2
| Nested (a::q) -> max (1 + maximal_depth a) (maximal_depth (Nested q));;which has this weird let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left max 1 (List.map regular_depth n);;which involves few more list functions in the definition. After writing this comment, I have a slight preference for the second version but I will let you choose your preferred option. |
|
Great! When I finished writing my previous answer, let rec regular_depth = function
| List _ -> 1
| Nested n -> 1 + List.fold_left max 1 (List.map regular_depth n);;was my preference, too (which I didn’t note to let you give an unbiased answer) |
|
@steffahn , would you mind adding a Changes entry in the |
Previously, the example dept function for `regular_nested` was clearly buggy. In particular the expression ``` 1 + max (maximal_depth a) (maximal_depth (Nested q)) ``` was adding `1` to the `maximal_depth (Nested q)` side, too, resulting in unbalanced depth calculation (later list elements get increasingly larger “depth”) The minimal fix would have been ``` max (1 + maximal_depth a) (maximal_depth (Nested q)) ``` but then I still find the `Nested [] -> 0` case confusing. Adding a `List []` element to produce `Nested [List []]` makes the depth jump by `2`!? We could redefine this. E.g. as `Nested [] -> 1`, which is better already. Still, this calls out a “Nested” case for certain values which aren’t actually reporting a depth>1. During PR review, we thus ended up with actually making `..._depth (Nested [])` evaluate to 2! A minimally changed definition thus looks like ``` let rec regular_depth = function | List _ -> 1 | Nested [] -> 2 | Nested (a::q) -> max (1 + maximal_depth a) (maximal_depth (Nested q));; ``` (about the name change, see below) We instead change the definition now to work with `List._` API instead, so it becomes ``` let rec regular_depth = function | List _ -> 1 | Nested n -> 1 + List.fold_left max 1 (List.map regular_depth n);; ``` This avoids the use of the previous, somewhat weird “manual fold” approach; additionally it makes the `Nested [] -> 2` case less prominent and thus (hopefully) less distracting to readers who don’t actually *want* do reason about the exact behavior of this depth-function. And finally, with this ``` let rec regular_depth = function | List _ -> 1 | Nested n -> 1 + …something…with…regular_depth…n…;; ``` structure, the function becomes (at least superficially) very similar to the subsequent ``` let rec depth = function | List _ -> 1 | Nested n -> 1 + depth n;; ``` which could help a reader to focus less on wondering if the latter really is just the result of directly “adapting” the former. The naming is also changed to keep more consistent: now it is `regular_nested` with `regular_depth` and then `nested` with `depth` Finally (also relating to naming) there actually was a clear typo that’s fixed now: the original text had one instance where it misspelled “`regular_nested`” as “`regular_depth`” in line 306 (now 305). (This commit also includes a few instances of bad-looking extra indentation removed, and the definition of `type 'a regular_nested` is not separated via `;;` from the example `let l = ...` which should help improve readability)
|
Added an entry… Ah, you said under 5.3.0, so should the entry be further down? |
|
It should but this is not an issue: I will move the entry to the right location when cherry-picking the change to the 5.3 branch. |
Improve `regular_nested` example for “Polymorphism” chapter of the manual (cherry picked from commit 6eaa14e)
|
Merged, changes entry moved, and cherry-picked to 5.3 in 8f12f9c. Thanks again for the patch ! |
|
thank you for the quick and responsive review :-) |
Previously, the example dept function for
regular_nestedwas clearly buggy. In particular the expressionwas adding
1to themaximal_depth (Nested q)side, too, resulting in unbalanced depth calculation (later list elements get increasingly larger “depth”)The minimal fix would have been
but then I still find the
Nested [] -> 0case confusing. Adding aList []element to produceNested [List []]makes the depth jump by2!?I could just re-define this as
Nested [] -> 1, but I believe that a mutually recursive definition is even easier to understand.In particular, now the new
is structurally (at least superficially) very similar to
which helps readers focus less on wondering if the latter really is just the result of directly “adapting” the former.
The naming is also changed to keep more consistent: now it is
regular_nestedwithregular_depth and thennestedwithdepth`Finally, there also was a clear type; the original text had one instance where it misspelled “
regular_nested” as “regular_depth” in line 306 (now 308).(This PR also includes a few instances of bad-looking extra indentation removed, and the definition of
type 'a regular_nestedis not separated via;;from the examplelet l = ...which should help improve readability)