Comments (6)
Hey @raphaelvoisin,
Thank you for raising this, I will do my best to answer your questions below.
Your questions
1. first() and last() behaviour
For the most part, collection methods return a new collection - this not only promotes a more consistent API but also favours applying multiple operations in succession. The methods that return scalars or keys/values from the collection are documented in the Methods Background section of the docs.
Ideally we would not use first()
or last()
unless as a last operation performed, and if you really want to extract the value you just do $collection->first()->current()
. However, the focus with loophp/collection is on iteration, so not extracting items would be best, similar to how you'd not want to get the value out of a monad.
This being said, I wouldn't be too bothered if we changed the behaviour of these methods in particular, just that it would have to be for a new major version only.
2. head() and tail() behaviour
These methods have consecrated definitions that stem from the list data type in functional programming. Head gives you the first element, tail gives you everything except the first element. See wikipedia.
Indeed tail() has nothing to do with last() and is not meant to be the mirror of head().
3. Missing autocompletion
I'm not sure if this is possible without anything else added, and you should not rely on simply calling ->current()
without then checking the type or using an assertion because the method can return null
(Psalm and PHPStan will also warn you about this). This is similar to the native PHP function current, which can return false
.
You have a few options:
a) use a native PHP assertion: assert($firstElement !== null);
b) use an assertion library like webmozarts/assert: Assert::notNull($firstElement);
c) use an if
statement: if ($firstElement !== null) {}
Using a) and c) should give you IDE autocompletion; using b) will not, but I personally prefer it because it works with static analysis and I don't care as much about the autocompletion.
4. contains() vs has()
Indeed these operations have different purposes - you can think of them like this:
- contains -> check if a given value exists in the collection; plain and simple, nothing more
- has -> check if the collection has one or more values based on some logic which can use the collection items' keys and/or values; this method takes callbacks with the purpose of specifying that logic
These methods both take variadic (any number of) parameters; combining them into a single one might be possible, but it would make the code messy and break the Single Responsibility Principle to some extent.
On illuminate/collections, it looks like it also uses has - not as an alias of contains, but with a different purpose of checking if a key exists.
My thoughts
I hope the above provides some clarity. Just wanted to add that loophp/collection offers many operations and possibilities, some of which might not be immediately obvious regarding their usefulness. As with learning any new API, it takes a bit of time to get comfortable with them. I can safely say I've probably used less than half of all of them in production code myself, however I do keep finding new opportunities to simplify my code and make it more "declarative" and "functional" by using them.
You've probably already seen the Methods API Docs, but in addition to those examples the unit tests and the static analysis tests can be an additional source of inspiration or clarification.
Lastly, feel free to open PRs for anywhere you feel the documentation is unclear or doesn't provide sufficient info!
from collection.
@AlexandruGG Thank you for your reply :)
1. first() and last() behaviour
Ideally we would not use first() or last() unless as a last operation performed, and if you really want to extract the value you just do $collection->first()->current().
Of course, but processing the first / last element on a list is a use-case so common that I would have kept something simple for them... maybe a personal opinion
2. head() and tail() behaviour
These methods have consecrated definitions that stem from the list data type in functional programming. Head gives you the first element, tail gives you everything except the first element. See wikipedia.
I see your point. BTW, do you know where this definition comes from ? Do you have examples of a similar API, in PHP or other language ?
3. Missing autocompletion
Thank you for explanation, it confirms that I didn't miss something obvious, and I'll continue with a simple if
, because I do care about autocompletion 😃
4. contains() vs has()
These methods both take variadic (any number of) parameters; combining them into a single one might be possible, but it would make the code messy and break the Single Responsibility Principle to some extent.
I think it's a question of taste here, but I see your point. I personnaly like to be able to use different parameter types as soon as static analysis is able to check everything, but I understand your choice.
My thoughts
I hope the above provides some clarity. Just wanted to add that loophp/collection offers many operations and possibilities, some of which might not be immediately obvious regarding their usefulness. As with learning any new API, it takes a bit of time to get comfortable with them.
I understand, I wanted to open the debate first to understand some choices. But an intuitive API is quite important too, it's part of the success of a library.
After your explanation, I would say that ->first
/ ->last
is probably what still disturbed me the most. (not being able to get the first/last element easily with autocompletion is a handicap to me 😄), but I understand the rest :)
from collection.
Of course, but processing the first / last element on a list is a use-case so common that I would have kept something simple for them... maybe a personal opinion
I agree. When I started building this library, one of the goal was the following: "methods within the library should only returns a CollectionInterface, never something else".
Then, and thanks to @AlexandruGG , I changed my mind and we decided which set of methods could possibly return another type of value.
This means that we can still improve that, nothing is definitely frozen. I also think that this is the right moment to do it, so we can have it ready for the next major release (7.0).
I see your point. BTW, do you know where this definition comes from ? Do you have examples of a similar API, in PHP or other language ?
I started writing this library first for learning purposes because I learned functional programming. These terms comes straight from Haskell.
- head: https://zvon.org/other/haskell/Outputprelude/head_f.html
- tail: https://zvon.org/other/haskell/Outputprelude/tail_f.html
Many other operations comes from it like foldLeft
, foldLeft1
, scanRight
, scanRight1
,...
Thank you for explanation, it confirms that I didn't miss something obvious, and I'll continue with a simple if, because I do care about autocompletion smiley
Sadly yes. I wish I could get rid of them and have a typed collection library working properly. The static analysis tools that we are using are making great effort to get better each days, and with time, these efforts will also benefits this library. Recently PSalm 5 introduced many changes for typing a pipe
operator, I guess in the long run this will have a huge impact on this library. We just have to wait a bit.
I think it's a question of taste here, but I see your point. I personnaly like to be able to use different parameter types as soon as static analysis is able to check everything, but I understand your choice.
I never recommend to people I work with to mix arguments types. It's harder to type for static analyzers tools and usually it makes your code even more messy. Unlike other languages we cannot do methods overloading in PHP, so I had to make a choice. You might or might not like it, this is what it is. We can surely improve things, but I had to start somewhere! :)
I understand, I wanted to open the debate first to understand some choices. But an intuitive API is quite important too, it's part of the success of a library.
I think these kind of discussions are a great value. It shows basically the point of view of the newcomers VS the point of view of the maintainers. This is the starting point of doing changes, if necessary. We are definitely open to the discussion as you can most probably feel from these back'n'forth posts. If you really would like to make your point, I also invite you to propose a PR with your changes so we can inline-review your proposal and even discuss further.
Regarding the success of the library, I didn't do it for that precisely. I wrote this library first to learn and share the discoveries I made by learning Haskell and the "lazy" principles. I'm glad that people are using it today, but I never planned to build this library to make it successful.
After your explanation, I would say that ->first / ->last is probably what still disturbed me the most. (not being able to get the first/last element easily with autocompletion is a handicap to me smile), but I understand the rest :)
How about proposing a PR about it and discuss it further? I have the feeling that this is something recurrent that could be easily fixed for the next major release. WDYT?
from collection.
@drupol thanks for adding to this discussion :).
@raphaelvoisin just to add to what's been said above:
I see your point. BTW, do you know where this definition comes from ? Do you have examples of a similar API, in PHP or other language ?
You will find them across languages that are functional or have incorporated functional elements. See in:
- Haskell: https://hackage.haskell.org/package/base-4.16.0.0/docs/Prelude.html#v:head
- Scala: https://docs.scala-lang.org/overviews/scala-book/collections-methods.html
- OCaml: https://ocaml.org/api/List.html (named
hd
andtl
-> they sure like shortening things 😄)
After your explanation, I would say that ->first / ->last is probably what still disturbed me the most. (not being able to get the first/last element easily with autocompletion is a handicap to me 😄), but I understand the rest :)
As Pol mentioned we could change this, but we should keep some consistency. For example, it might be weird if we change head()
to not return a Collection, while tail()
returns a Collection. At the same time, we cannot change the latter.
Perhaps a solution would be to not make head()
be a simple alias for first()
anymore, and only make first()/last()
return an item from the collection.
If you'd like to contribute this change, have a look into the code and see if you can open a draft PR, we'd be happy to help further from there :)
from collection.
Since this issue has not had any activity within the last 5 days, I have marked it as stale.
I will close it if no further activity occurs within the next 5 days.
from collection.
Hey @raphaelvoisin, I hope this was fine to close but let us know if you think it should be kept open.
from collection.
Related Issues (20)
- `Partition` Operation - Awkward to use? HOT 18
- Add find/search/where/single/firstWhere method HOT 23
- Modify `all` operation to prevent data loss HOT 6
- PHPStan 1.0 upgrade HOT 1
- Typed collection support HOT 17
- Reduction operations should return a single value HOT 9
- Issue with cache and fromCallable HOT 13
- PHPStan reporting an error for missing optional parameters HOT 6
- [Question] Rename Collection interface to CollectionInterface HOT 1
- Dependency Dashboard HOT 54
- Collection interface doesn't extend Countable HOT 9
- Weird interplay between Collection and PDO result set HOT 13
- Plus operation RFC HOT 10
- Palm cannot infer types when using some operations HOT 4
- Unexpected behavior of pair operation over empty collection HOT 1
- [Feature request] Implement stable sorting HOT 12
- Memory size exhausted for large collections HOT 15
- Issue with distinct HOT 3
- Distinct is slow HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from collection.