Skip to content
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

Add Cmd.Extra.andThen #66

Merged
merged 2 commits into from
Oct 2, 2024
Merged

Add Cmd.Extra.andThen #66

merged 2 commits into from
Oct 2, 2024

Conversation

Janiczek
Copy link
Contributor

@Janiczek Janiczek commented Oct 2, 2024

From Slack:

@Janiczek 20:53

@gampleman re core-extra, would Cmd.Extra.andThen be an acceptable addition?

andThen : (model -> ( model, Cmd msg )) -> ( model, Cmd msg ) -> ( model, Cmd msg )
andThen fn ( model, oldCmd ) =
   let
        ( newModel, newCmd ) =
            fn model
    in
    ( newModel
    , Cmd.batch [ oldCmd, newCmd ]
    )

@rupertlssmith 20:58

andMap is handy too...

@gampleman 20:59

I can see that kind of function being useful. The only question in my mind would be about the naming since andThen gives vibes of laws of monads and this doesn’t look like it would quite qualify.

@Janiczek 21:00

happy for it to have any other name

@miniBill 22:38

This is monad-ish if you consider M = (model, Cmd msg) without a type parameter

@knuton 08:52

I have always thought of it as monadic, is it not?
The Return library (Return msg model = ( model, Cmd msg )) has this exact andThen:
https://package.elm-lang.org/packages/Fresheyeball/elm-return/latest/Return#andThen
https://github.com/Fresheyeball/elm-return/blob/79aa468b7118108c1155de3e79b5ee6ac50cdcfb/src/Return.elm#L202-L239 (edited)

@Janiczek 09:09

It (and my version above) only lets you react to the model part of the (model, Cmd msg). (Which is probably to be expected since Cmd is opaque... but imagine you had your own Effect instead, then there would be ways to inspect it)
This is with no mathematic rigor, but I imagine it would only be "properly" monadic if it was (model, Cmd msg) -> (model, Cmd msg), not model -> (model, Cmd msg)
Anyways, probably doesn't matter. I find the andThen name to be relatively undesrtandable/fitting in this context, even if not properly monadic (edited)

@knuton 09:21

Hm, but the model -> (model, Cmd msg) is exactly what makes it monadic I would say
Yes, there are two type parameters, so you have to assume one is fixed
But that's the same as Result.andThen, for example: https://package.elm-lang.org/packages/elm/core/latest/Result#andThen
09:24
The only "problem" I can see with your snippet is that it is too specific, not allowing the model type to change
It could be
andThen : (a -> ( b, Cmd msg )) -> ( a, Cmd msg ) -> ( b, Cmd msg )

@rupertlssmith 10:15

^ That is andMap

@Janiczek 10:16

is it? 👀 I'd expect andMap to be the applicative thingy

@rupertlssmith 10:17

Yeah, you are probably right. So what would andMaps type be in that case?
Probably is best to allow andThen to change the model type, still works for the case where you keep the type the same.

@knuton 11:23

andMap takes a boxed function and applies it to a boxed parameter
https://en.wikipedia.org/wiki/Applicative_functor#Definition
Which just requires batching the commands from both "boxes" in this case
The Return library has this one as andMap too: https://github.com/Fresheyeball/elm-return/blob/79aa468b7118108c1155de3e79b5ee6ac50cdcfb/src/Return.elm#L112-L116

@gampleman 22:06

Let's see:

  1. Cmd.Extra.pure a |> Cmd.Extra.andThen b == b a seems ✅ as long as we allow changing the type signature of the model.
  2. m |> Cmd.Extra.andThen Cmd.Extra.pure == m again seems ✅
  3. (m |> Cmd.Extra.andThen g) |> Cmd.Extra.andThen h == m |> Cmd.Extra.andThen (\x -> g x |> Cmd.andThen h) I'm not a 100% sure I can eye-ball this one correctly. Would be a bit tricky to test this as well.

@Janiczek 22:15

((m,[c]) >>= g) >>= h == (m,[c]) >>= (\mx -> g mx >>= h)

(mg, [c,cg]) >>= h == (m,[c]) >>= (\mx -> (mxg, [cxg]) >>= h)

(mgh, [c,cg,cgh]) == (m,[c]) >>= (\mx -> (mxgh, [cxg, cxgh]))

-- mx becomes m, cx becomes c

(mgh, [c,cg,cgh]) == (mgh, [c,cg,cgh])

I think that's going to be alright? Even the order of cmds is the same which I wouldn't bet on beforehand 😅
this is with the assumption that

(m,[c]) >>= g
-->
(mg,[c,cg])

that is,

andThen f (m,c) =
  let (mf,cf) = f m in
  (mf,[c,cf])

playing bit loose with the notation (I'm on mobile) (edited)

@gampleman 22:18

In which case I'm reasonably happy to add it as andThen provided we allow the model type variable to change

[... discussion continues on tangent ...]

@gampleman gampleman merged commit e9e54c7 into elmcraft:master Oct 2, 2024
2 checks passed
@gampleman
Copy link
Collaborator

@lue-bird I updated the description as this was a result of a decision made on Slack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants