wernerdegroot / listzipper

MIT License
39 stars 8 forks source link

[Feature] map with different functions for selected and not selected elements #27

Open andys8 opened 5 years ago

andys8 commented 5 years ago

Hi, there,

great library! I'm missing a mapping function where one can provide two different functions for mapping elements, depending on the element being selected or not.

My use case is a view that renders the selected element different. It won't work with mixing mapCurrent with mapBefore/After (without an intermediate step), because they're a -> a. map is from a -> b but the information if it's current is not provided.

Examples from other libraries

mapCS : (a -> b) -> (a -> b) -> Pivot a -> Pivot b

https://package.elm-lang.org/packages/yotamDvir/elm-pivot/latest/Pivot#mapCS

selectedMap : (Position -> SelectList a -> b) -> SelectList a -> List b

https://package.elm-lang.org/packages/miyamoen/select-list/latest/SelectList#selectedMap

Implementation

Here are three sample implementations for the feature. The api, name and implementation of the function can be discussed :)

mapBoth1 : { mapCurrent : a -> b, mapElse : a -> b } -> Zipper a -> Zipper b
mapBoth1 { mapCurrent, mapElse } zipper =
    let
        mapFn a =
            if a == Zipper.current zipper then
                mapCurrent a

            else
                mapElse a
    in
    Zipper.map mapFn zipper
mapBoth2 : { mapCurrent : a -> b, mapElse : a -> b } -> Zipper a -> Zipper b
mapBoth2 { mapCurrent, mapElse } zipper =
    Zipper.from
        (zipper |> Zipper.before |> List.map mapElse)
        (zipper |> Zipper.current |> mapCurrent)
        (zipper |> Zipper.after |> List.map mapElse)
mapBoth3 : (Bool -> a -> b) -> Zipper a -> Zipper b
mapBoth3 f zipper =
    Zipper.map (\a -> f (a == Zipper.current zipper) a) zipper
wernerdegroot commented 5 years ago

Hi, @andys8,

Sorry for not getting back to you sooner. I like your proposal. Would you prefer an object with mapCurrent and mapElse over two functions (that, admittedly, can be ambiguous as the type signature is the same)? In any case, I think I prefer the function signature of mapBoth1 and mapBoth2 over that of mapBoth3.

All function names (mapBoth, mapCS and selectMap) are fine with me (although I wouldn't be able to predict what the function does from neither of those names)

With regards to the implementation: I think that destructuring the Zipper would be easiest. In that case you don't have to check each element to see if it is the focus of the zipper (you know that already).

Would you be willing to make a pull request for this?

rjdellecese commented 4 years ago

I realize that this is basically a :+1: comment, so apologies if it's unwelcome, but I also just ran into an occasion where I would have liked a function like the ones proposed here!

rjdellecese commented 4 years ago

I'm working on a different project and I once again found that I would have loved to have this function. Should you find the time to merge https://github.com/wernerdegroot/listzipper/pull/29 @wernerdegroot, I would definitely be grateful for it!