-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
[stdlib] Add List.map(fn(mut T)->None)
#3839
base: nightly
Are you sure you want to change the base?
Conversation
Signed-off-by: rd4com <[email protected]>
9d20a4a
to
786e580
Compare
List.map(fn(mut T)->None)
List.map(fn(mut T)->None)
In order to get feature parity with python, we probably want lazy iterators. My vote would be for copying the Rust API where you need to do a Could we do something like making that return a |
I think it's a good idea to deviate form Python and add
+1 I think it's worth making it an iterator return and not a copy of the data. We should also aim to implement Python's builtin map function (also
I think this will dovetail nicely with #3653 (the part about implicit constructors from iterators), which would mean for people who want to just declare the variable type they can, and those who want to use functional style collect also can :) The approach I'll propose can become ugly very fast once we do it for many types, but I think it's worth trying to implement it and testing it out: What if we implement one specific type of iterator for each op, for this PR the case would be Or, we could just let the function return a new List and come back to iterate and fix it once we have Iterators better defined. WDYT? |
I agree, the builtin map can take an iterable (remember that iterators are iterable and the
I'd rather have a generic iterator, so that |
@@ -930,6 +930,35 @@ struct List[T: CollectionElement, hint_trivial_type: Bool = False]( | |||
""" | |||
return self.data | |||
|
|||
fn map(ref self, func: fn (mut T) -> None) -> Self: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: The API you're describing seems more like for_each
than map
.
fn(mut T) -> None
is a common for_each
signature, whereas map
tends to be fn[U: CollectionElement](T) -> U
.
Examples:
- Rust for_each and map
- Swift for_each and map
- C++ ranges::for_each and ranges::transform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think it was just a mistake in assuming the output will be the same type as the input collection type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not exactly. for_each
tends to mutate in place, while map
tends to create a new allocation. Users associate both APIs with those characteristics in my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that I think the OP meant map as in map a func to return a new instance with copied values changed, just that he forgot the use case where the collection type of the result might be different.
personal note: I'd actually like us to call for_each
apply
because it's a name used in the pandas and numpy ecosystem and is IMO much more intuitive (see PR #3877)
@owenhilyard I don't want to get too into the weeds on this and derail the main topic of this PR, but I tend to think the cleanest API would be a constructor for edit: To be clear this is just a hypothetical lambda syntax. |
+1 to
I agree short list comprehensions are cleaner, but I find they get messy once you want to do |
The items = [1, 2, 3, 4, 5]
inc = list(map(lambda x: x+1, items)) That way, there is more time to think about the chaining thing ? |
I prefer to do method chaining, since it means that I'm not reading my code inside-out. In terms of this particular PR, I agree it should be |
I want to repeat this point, I strongly dislike this being called |
|
I'd argue that many more people use Python (pandas, numpy, ML frameworks) and R (it's a builtin) than Haskell... |
The other side of the fence is JS, TS, Rust, C++, Java, C#, most FP languages, Scala, Kotlin, and Swift, among others. Ocaml uses |
Ok that's a long list 😆. But I still stand by my opinion, I think fn add_two(value: Int) -> Int:
return value + 2
fn bigger_smaller_or_eq(value: Int) -> String:
return "bigger" if value > 4 else "smaller" if value != 4 else "equal"
fn main():
items = List[Int](1, 2, 3)
print(items.map(add_two)) # 3 4 5
items.apply(add_two)
print(items) # 3 4 5
# items.apply(bigger_or_smaller) # won't compile
print(items.map(bigger_smaller_or_eq)) # smaller equal bigger |
Hello, the pr is an implementation for
map
,we could have used parameters, but it is for a first pythonic experience 🐍
(progressive exposure to complexity and build on current python skills already learned)
map
Apply a function to every elements of
self
and returns a newList