elm/core

Is there a reason we don't have Maybe.apply?

JoshSmithCode opened this issue · 6 comments

apply : Maybe a -> Maybe (a -> b) -> Maybe b
apply =
    Maybe.map2 (|>)

Since maybe is an applicative functor, could we have a Maybe.apply defined in core? I know it's simple enough to define externally and maybe it makes more sense in elm-community/maybe-extra but I thought I'd ask here first anyway.

Thanks for reporting this! To set expectations:

  • Issues are reviewed in batches, so it can take some time to get a response.
  • Ask questions a community forum. You will get an answer quicker that way!
  • If you experience something similar, open a new issue. We like duplicates.

Finally, please be patient with the core team. They are trying their best with limited resources.

what is this useful for

type alias RoofParameters = 
    { majorSpan : Float
    , minorSpan : Float
    , pitch : Float
    , overhang : Float
    , rafterSpacing : Float
    , rafterWidth: Float
    }

getValidRoofParameters : Model -> Maybe RoofParameters
getValidRoofParameters model = 
    Just RoofParameters
        |> apply (model.majorSpan)
        |> apply (model.minorSpan)
        |> apply (model.pitch)
        |> apply (model.overhang)
        |> apply (model.rafterSpacing)
        |> apply (model.rafterWidth)

See above for example. If in the model we store floats as a Maybe, the user may not have entered anything for that float in particular yet. If we have apply, we can build up the RoofParameters type based on the floats. If any are missing, we get Nothing. Alternatively, we could do something like

getValidRoofParameters : Model -> Maybe RoofParameters
getValidRoofParameters model = 
case [model.majorSpan, model.minorSpan, model.pitch, model.overhang, model.rafterSpacing, model.rafterWidth] of 
    [Just majorSpan, Just minorSpan, Just pitch, Just overhang, Just rafterSpacing, Just rafterWidth] -> 
        Just (RoofParameters majorSpan minorSpan pitch overhang rafterSpacing rafterWidth)

    _ ->
        Nothing

But if we start grouping things based on a list etc. we run the risk of, for example, missing one of the Maybe Float's and having the compiler reporting this as still a valid comparison.

I know if we were working with less variables we could use Maybe.map5 here, but considering that sometimes you can't just remove variables from a calculation, doesn't it make sense to have a way to pipe more? This may also remove the need for so many tiers of Maybe.mapX

This already exists as Maybe.Extra.andMap, with examples of how to use it as an arbitrarily big mapN or andThenN.

I think it's rare enough to want more than 5 arguments that it's fine to have it only in Maybe.Extra. Learning how custom types work with Maybe is one of the first sticking points for many people new to Elm and functional languages, so it's really valuable that the Maybe module is so simple.

@skyqrose said it really well!

A long time ago when we were trying to figure out a name for >>= and <*>, we came up with andThen and felt it worked really well! But we did not come up with something that felt right for <*>, and felt that it would be more confusing to go with a bad name than to just not have it in the core libraries. With the function being available in -extra pacakges and with the "decode pipeline" style becoming popular, this felt like a balance that worked well for beginners and experts who may disagree about how they would like to mapN.

It could be named tryParameter