-
Notifications
You must be signed in to change notification settings - Fork 3
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
WIP: List extra basic #8
base: master
Are you sure you want to change the base?
Conversation
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.
Thanks, this is super!
I've made a bunch of suggestions -- see what you think. One theme is that you could take more advantage of your access here to the internals of DictList
-- sometimes it would be more efficient to operate on the dict and the list separately, and then reconstruct a DictList
to return.
# ListExtra | ||
|
||
@docs last, inits, (!!), uncons, maximumBy, minimumBy, andMap, andThen, takeWhile, dropWhile, unique, uniqueBy, allDifferent, allDifferentBy | ||
|
||
-} |
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.
Ultimately, I think it probably makes sense to try to classify these under topical headings, thus splitting them up throughout the documentation -- it's nice to keep track of their origin in List.Extra
in the source, but I think it's better in the documentation if they are divided topically.
-} | ||
|
||
import Dict exposing (Dict) | ||
|
||
import Dict exposing (Dict, keys) |
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.
It would be better not to expose keys
, since we have our own DictList.keys
as well. So, we can refer to Dict.keys
explicitly.
import DictList.Compat exposing (customDecoder, decodeAndThen, first, maybeAndThen, second) | ||
import Json.Decode exposing (Decoder, keyValuePairs, value, decodeValue) | ||
import List.Extra | ||
import Set |
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.
This appears to be unused so far?
@@ -535,6 +557,11 @@ getAt index (DictList dict list) = | |||
|> Maybe.map (\value -> ( key, value )) | |||
) | |||
|
|||
{-| Alias for getAt, but with the parameters flipped. | |||
-} | |||
(!!) : DictList comparable value -> Int -> Maybe ( comparable, value) |
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.
Looks like this needs a run of elm-format
...
@@ -938,6 +965,174 @@ fromDict dict = | |||
|
|||
|
|||
|
|||
-- LIST EXTRA | |||
|
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.
Just to keep things organized nicely, we could move getAt
and (!!)
and any other things actually inspired by List.Extra
down here -- it will help a bit when changes are made to List.Extra
and we want to track them. (So, I'd suggest organizing the source to match where we got things from, whereas re-arranging the documentation topically).
I actually had considered putting the List.Extra
stuff in a separate module, but I can see now that that won't work, because we'll want to make use of the DictList
constructors which we don't expose (so aren't accessible from another module).
unique [0,1,1,0,1] == [0,1] | ||
-} | ||
unique : DictList comparable1 comparable2 -> DictList comparable1 comparable2 | ||
unique list = |
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.
This one doesn't really make sense in this form, because the keys will necessarily be unique -- so this will always be an identity function as implemented here -- it will never remove anything.
I suppose we could change it so that it only considers the values -- the puzzle then would be which key should be chosen where two keys have equal values ...
uniqueBy f list = | ||
toList list | ||
|> List.Extra.uniqueBy (uncurry f) | ||
|> fromList |
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.
This one, on the other hand, could remove some values, so it could do something. I wonder which value the implementation retains (since the "duplicate" values are not, themselves, necessarily equal)? That would be worth documenting (I suppose List.Extra
might not document it).
-} | ||
allDifferent : DictList comparable1 comparable2 -> Bool | ||
allDifferent list = | ||
-- @TODO Should this method just use the values, or also the keys |
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.
This is a very good question!
In some cases, it makes sense to have two versions of a function -- for instance, I ended up with both getAt
and getKeyAt
. So, you might want to have an allDifferent
which considers both keys and values, and then also an allDifferentValues
that considers only values, or something like that.
-- |> toList | ||
-- |> values | ||
-- |> concat | ||
|
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.
Ah, yes, it isn't obvious to me either what andMap
and andThen
ought to do in the context of DictList
-- that would take some thinking.
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.
This is such an interesting question! I've been thinking about that and talking with a bunch of people.
One sensefull type would look like the following: (DictList comparable (a -> b)) -> (DictList comparable a) -> (DictList comparable b)
. For the concrete implementation you would probably match the keys and throw away all (a->b)'s / a's when there are no matching keys. This requires for example no instance of pure
(the other function which makes sense in the context). To implement pure
you would need some mempty
for the keys.
Note: Dict.Map doesn't implement that in haskell
Note2: https://hackage.haskell.org/package/total-map-0.0.6/docs/Data-TotalMap.html does implement the above idea
For andThen
the signature could look like the following:
andThen : (comparable -> a -> DictList comparable b) -> DictList comparable a -> DictList comparable b
What could this function do:
- This function maps a key/value pair to a new DictList, so it kinda generates a new dict list.
- The result could then have the specific key removed and inserted all keys/values of the result in b. This smashing together could be some kind of
merge
operation.
In this world you could reimplement:
remove k dictList = andThen (\k2 v -> if k == k2 then empty else fromList [(k2, v)]) dictList
update k v dictList = andThen (\k2 v2 -> fromList [(k2, if k == k2 then v else v2)]) dictList
Does it even remotely makes sense?
TL;DR
- Don't expect to have some form of product/cartesian behaviour for
andMap
- I doubt there is too much value in
return
/pure
implementations
@@ -0,0 +1,82 @@ | |||
module ListExtraTests exposing (tests) | |||
|
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.
What I would suggest here is looking at https://github.com/elm-community/list-extra/blob/6.0.0/tests/Tests.elm and:
- copy the relevant tests
- make whatever the smallest changes are to fit the function signatures here
That is, instead of working from ListTests.elm
as a base, work from the actual tests for List.Extra
-- the idea is to prove that we can pass the List.Extra
tests (modified as little as needed to fit our context).
Then, in a separate test file, you could test anything else you want to test -- i.e. things that List.Extra
doesn't test, or behaviours that are unique to DictList
.
I'm attending a conference this week, so I'm not sure I'll find time to respond to your long and detailed review. I'm super happy to retrieve that insightful comments that quickly, so happy to learn new things. |
This adds the list extra basic stuff, as described in #4 (comment)
This is WIP for example because of the missing test coverage