Created
August 1, 2017 22:51
-
-
Save jbrains/1a4e2f4c22705e6d5cac6652db607bed to your computer and use it in GitHub Desktop.
How do I move the composition into the pipeline operating on Maybes?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
changePlayerName : PlayerModel -> String -> ScorekeeperModel -> ScorekeeperModel | |
changePlayerName playerToChange newName scorekeeperModel = | |
let | |
{ players } = | |
scorekeeperModel | |
in | |
{ scorekeeperModel | |
| players = | |
Exts.List.mergeBy | |
(.id) | |
players | |
[ { playerToChange | name = newName } ] | |
} | |
leaveEditingMode : ScorekeeperModel -> ScorekeeperModel | |
leaveEditingMode scorekeeperModel = | |
{ scorekeeperModel | |
| playerToEdit = Nothing | |
, newNameOfPlayer = "" | |
} | |
-- This is the part that feels weird. How do I inline this into the pipeline below? | |
changePlayerNameThenLeaveEditingMode : PlayerModel -> String -> ScorekeeperModel -> ScorekeeperModel | |
changePlayerNameThenLeaveEditingMode playerToChange newName = | |
changePlayerName playerToChange newName | |
>> leaveEditingMode | |
-- deep inside an update function | |
-- newNameOfPlayer is a String | |
-- playerToChange is a PlayerModel | |
-- scorekeeperModel is a ScorekeeperModel | |
-- this expression should return a ScorekeeperModel | |
scorekeeperModel | |
|> (newNameOfPlayer | |
|> Exts.Maybe.validate (not << String.isEmpty) | |
|> Maybe.map (changePlayerNameThenLeaveEditingMode playerToChange) | |
|> Maybe.withDefault identity | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your types are not lining up, as far as I can tell.
One thing that I find helpful here is that the order of applying
Maybe.map
and function composition shouldn't matter.That is,
Maybe.map (f >> g) == (Maybe.map f) >> (Maybe.map g)
(and that will hold true for<<
as well).So looking at
Maybe.map (changePlayerName playerToChange >> leaveEditingMode)
, I think of it as first using Maybe.map on each of the functions being composed, then composing the resulting Maybe functions. So for the whole thing to typecheck, each of the components has to typecheck.The type of
changePlayerName playerToChange
isString -> ScorekeeperModel -> ScorekeeperModel
- it takes a String and a ScorekeeperModel, then returns an updated ScorekeeperModel. We could also say "it takes a String, and returns a function that takes a ScorekeeperModel and returns a ScorekeeperModel". This latter phrasing seems odd, but it's the more appropriate one when we think of the Maybe type we have:Exts.Maybe.validate (not << String.isEmpty) : String -> Maybe String
Because
Maybe.map
takes a function on base types,a -> b
, and returns a function that turns aMaybe
of typea
into aMaybe
of typeb
.So
Maybe.map (changePlayerName playerToChange)
takes aMaybe String
, and returns aMaybe (ScorekeeperModel -> ScorekeeperModel)
. (This is, in fact, just the type ofchangePlayerNameThenLeaveEditingMode playerToChange
- which is why the code above works).But it breaks down when you try compose that with
Maybe.map leaveEditingMode
Because the type of that must necessarily be
Maybe.map leaveEditingMode : Maybe ScorekeeperModel -> Maybe ScorekeeperModel
whereas for the composition to work we would need the type to be
(ScorekeeperModel -> ScorekeeperModel) -> (ScorekeeperModel -> ScorekeeperModel)
.What that type looks like to me is a function which takes a "ScorekeeperModel updater", and returns a different one. This is what @SamirTalwar's suggestion does - it gives to the second
Maybe.map
a function that says "compose something with leaveEditingMode". That something is also a "ScorekeeperModel updater", and they compose just in the way we need. (I think possibly you might want the arrow in the opposite direction, to apply leaveEditingMode last.)HTH