Raku / problem-solving

🦋 Problem Solving, a repo for handling problems that require review, deliberation and possibly debate
Artistic License 2.0
69 stars 16 forks source link

Should `.flatmap` be really DEPRECATED or not? #430

Open lizmat opened 3 weeks ago

lizmat commented 3 weeks ago

Some history about .flatmap. Before the Great List Refactor (GLR) the .flatmap method had slightly different semantics than it has now. Probably as part of the GLR, @jnthn changed it to the current semantics

Until this documentation change the .flatmap method was documented as DEPRECATED when in fact it has never been marked as such. And its semantics where that as were suggested in the documentation.

My original thought was: well, that's easy! A simple documentation simplification and all is well.

However, it has been brought up that maybe it would be a good idea to DEPRECATE .flatmap in 6.d and remove in 6.e.

Hence this Problem Solving Issue

vrurg commented 3 weeks ago

To me the explanation in the original version of the doc is good enough to support deprecation. I just wonder how much is it used in the ecosystem?

raiph commented 3 weeks ago

Is there a Raku deprecation policy document? I mean something outside of the user doc. If there is, I'll probably delete/hide this comment.

Am I right that info about deprecations appears in multiple locations?:

1. Rakudo

Liz introduced a trait that can be applied to some declarations (just routines and attributes I think) to mark them as deprecated. Code that uses a deprecated routine/attribute then produces a warning/error message when the code is run. User code can use it, and she's also used it in Rakudo to mark some built in routines/attributes as deprecated.

But, aiui, this was not applied to the flatmap built in. (And presumably still isn't.)

And I'm guessing the existing trait has not been applied to some other built ins that are "supposed" to be deprecated.

And I'm guessing there are features that aren't particular routines or attributes that are supposed to have been deprecated but instead other features that the is DEPRECATED trait can't (yet?) be applied to, and either have custom coding to get a deprecation message generated, or haven't yet been marked in Rakudo.

I wonder if it would be sensible for there to be something like a my @*DEPRECATED; proto trait_mod:<is> (|($declaration, :$DEPRECATED!) { @*DEPRECATED.push: $declaration; {*} }; CHECK .say for @*DEPRECATED if %*ENV<DEPRECATED>; that accumulates all declarations of deprecated features (as against uses, and then report that list at something like CHECK time so it's easy to produce a list of all deprecated features in a program, including all such features if the program is Rakudo?

2. Roast

Roast is supposed to be the spec, not Rakudo.

Perhaps deprecated annotations are supposed to appear in roast before they appear in Rakudo.

But I don't recall seeing any. If that's right, then maybe we can figure out how to have an is DEPRECATED trait that can be conditionally applied to some test directory, file, or even individual test in roast?

3. User doc

The user doc mentions deprecations.

4. Discussions

Like this issue.


So, multiple locations for recording info about deprecations, all subject to being incomplete, wrong, contradictory, right?

librasteve commented 2 weeks ago

thought I would start my thoughts by looking at the docs - under type Any (https://docs.raku.org/type/Any#routine_map), we have

routine map
method deepmap
method duckmap
method nodemap
method flat

yet method flatmap is documented over here https://docs.raku.org/routine/flatmap ... bit odd that

but that got me to thinking about the family of special maps aka xxxxmap, so I stuck this query into ChatGPT describe coding use of deepmap duckmap nodemap flatmap (dear reader: I would encourage you to try this)

the most germane parts of ChatGPT's opinion is:

  1. flatMap flatMap is a common functional programming operation that maps a function over a collection and then flattens the result by one level. It is a combination of map followed by flatten (or concat in some contexts).

Example in JavaScript: const array = [1, 2, 3, 4]; const result = array.flatMap(x => [x, x * 2]); console.log(result); // [1, 2, 2, 4, 3, 6, 4, 8]

Example in Python: array = [1, 2, 3, 4] result = [item for sublist in map(lambda x: [x, x * 2], array) for item in sublist] #huh? print(result) # [1, 2, 2, 4, 3, 6, 4, 8]

and

Summary deepMap: Recursively applies a function to each element in a nested data structure. duckMap: Not a standard term, possibly domain-specific or based on duck typing principles. nodeMap: Applies a function to each node in a hierarchical structure (like a tree). [wrong!] flatMap: Applies a function to each element and flattens the result by one level.

My conclusion from this and other research is that flatmap is a fairly common feature in functional languages and I suppose it can be considered one of the raku xxxxmap features.

So I think it would be a small positive to keep it and a very low benefit to remove it.

~librasteve

Leont commented 2 weeks ago

I vehemently oppose deprecating this, as stated in the previous discussion on the subject:

… the alternatives are all harder to read. @foo.map({ ... }).flat is hard to read because of end-weight issues, especially if the map block contains multiple lines. flat @foo.map({ ... }) contains a reversal of order (left to right for the method call and but right to left for function call), a break of the method chain and has subtle precedence issues. flatmap is usually the most readable option available in my experience.

I would also like to note that this method is present in several other modern languages such as Javascript, Rust, Kotlin and Scala (and probably many others); I'm genuinely puzzled by the suggestion that it's confusing when they don't find it confusing.

Leont commented 2 weeks ago

My conclusion from this and other research is that flatmap is a fairly common feature in functional languages and I suppose it can be considered one of the raku xxxxmap features.

Yeah, I think it's the kind of feature you'll use often if you have a functional programming state of mind, but not at all if you don't.