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

Initial value in a channel ignored in foldp #148

Closed
rehno-lindeque opened this Issue Jan 28, 2015 · 11 comments

Comments

Projects
None yet
6 participants
@rehno-lindeque
Contributor

rehno-lindeque commented Jan 28, 2015

The initial value in a Channel doesn't appear in a foldp over the channel converted to a Signal.

type Action = Init
            | ...

actions : Signal.Channel Action
actions = Signal.channel Init

update : Action -> Model -> Model
update action state =
  case Debug.log "action" <| action of      -- Debug.log never prints "action: Init"
    Init -> ...

model : Model -> Signal Action -> Signal Model
model initialState action =
  Signal.foldp update initialState action  -- Seems like the initialState here should be
                                           -- combined with the initial action, either
                                           -- to create the first value or to create the
                                           -- first two values in the signal

main = run emptyModel (Signal.subscribe actions)

Initializing a signal with a good initialState is not always easy because initialization may depend on a signal coming in from a port in which case you need to do something like this:

port userId : Signal Int

main = run emptyModel (Signal.zip (Signal.subscribe actions) userId)

...in this case creating an initial model based on the incoming userId seems near impossible unless some event can be triggered (perhaps by explicitly calling send on the userId port after the widget has been initialized in order to force an update).

This might assist with issues like this one as well: https://github.com/elm-lang/core/issues/91. I'm not taking the work on promises into account, I don't know if this may be handled already in the upcoming implementation.

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@rehno-lindeque

This comment has been minimized.

Show comment
Hide comment
@rehno-lindeque

rehno-lindeque Jan 28, 2015

Contributor

Oh thanks! That one totally slipped my mind, I gave it a try:

This did indeed do the trick...

Signal.foldp' update (flip update emptyModel) (Signal.zip action ownerId)

This still feels like some pretty strange behavior though? It completely blind-sided me that the first value in the signal was ignored by the fold.

Contributor

rehno-lindeque commented Jan 28, 2015

Oh thanks! That one totally slipped my mind, I gave it a try:

This did indeed do the trick...

Signal.foldp' update (flip update emptyModel) (Signal.zip action ownerId)

This still feels like some pretty strange behavior though? It completely blind-sided me that the first value in the signal was ignored by the fold.

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Jan 28, 2015

Member

The "initialization" phase is different from the "running" phase. Imagine writing this code foldp (\_ c -> c + 1) 0 Mouse.clicks and having the initial value be 1!

In the TodoMVC app, I bring the initial value in as a value, not a signal, and that works great. Is that not plausible for your case?

Member

evancz commented Jan 28, 2015

The "initialization" phase is different from the "running" phase. Imagine writing this code foldp (\_ c -> c + 1) 0 Mouse.clicks and having the initial value be 1!

In the TodoMVC app, I bring the initial value in as a value, not a signal, and that works great. Is that not plausible for your case?

@rehno-lindeque

This comment has been minimized.

Show comment
Hide comment
@rehno-lindeque

rehno-lindeque Jan 28, 2015

Contributor

Unfortunately not really - we have a button that allows admins to act on behalf of another user or as a guest (so the acting user is not fixed). To be honest, I could probably change the logic so that the owner id is assigned on save rather than initialized in the widget - this is probably a better behavior that didn't occur to me.

I see what you're saying with foldp (\_ c -> c + 1) 0, that does make sense. I was just a little surprised...

I won't spend any time on this because I'm sure you've thought about this much more deeply than I could ever hope to, but it does almost seem to me as if one should be able to have some kind of Mouse.click : DelayedSignal () where foldp (\_ c -> c + 1) 0 Mouse.click really does start out as 0 with the alternate behavior for Signal (), similar to List.foldl (\_ c -> c + 1) 0 []. Perhaps a kind of newtype DelayedSignal?

Contributor

rehno-lindeque commented Jan 28, 2015

Unfortunately not really - we have a button that allows admins to act on behalf of another user or as a guest (so the acting user is not fixed). To be honest, I could probably change the logic so that the owner id is assigned on save rather than initialized in the widget - this is probably a better behavior that didn't occur to me.

I see what you're saying with foldp (\_ c -> c + 1) 0, that does make sense. I was just a little surprised...

I won't spend any time on this because I'm sure you've thought about this much more deeply than I could ever hope to, but it does almost seem to me as if one should be able to have some kind of Mouse.click : DelayedSignal () where foldp (\_ c -> c + 1) 0 Mouse.click really does start out as 0 with the alternate behavior for Signal (), similar to List.foldl (\_ c -> c + 1) 0 []. Perhaps a kind of newtype DelayedSignal?

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Aug 30, 2015

Contributor

Can this issue be closed?

Contributor

jvoigtlaender commented Aug 30, 2015

Can this issue be closed?

@rehno-lindeque

This comment has been minimized.

Show comment
Hide comment
@rehno-lindeque

rehno-lindeque Aug 31, 2015

Contributor

I'd say this is close enough to language design that it probably doesn't belong here anyway. There's been a large number of proposals for alternate API's related to this, perhaps there will be a top-level issue in elm-plans at some point.

Contributor

rehno-lindeque commented Aug 31, 2015

I'd say this is close enough to language design that it probably doesn't belong here anyway. There's been a large number of proposals for alternate API's related to this, perhaps there will be a top-level issue in elm-plans at some point.

@amitaibu

This comment has been minimized.

Show comment
Hide comment
@amitaibu

amitaibu Oct 3, 2015

For posterity, I was able to "solve" a similar case where I needed an initial value to be passed to the ports, by simply sending a default value immediately after starting Elm:

var elmApp = Elm.fullscreen(Elm.Main, {somePort: null});

// Make sure that the ports are getting the inital values, as by design they are being fed only with 
// changed values.
elmApp.ports.somePort.send(null);

It's a little hackish, but still allows one to use the start-app module.

amitaibu commented Oct 3, 2015

For posterity, I was able to "solve" a similar case where I needed an initial value to be passed to the ports, by simply sending a default value immediately after starting Elm:

var elmApp = Elm.fullscreen(Elm.Main, {somePort: null});

// Make sure that the ports are getting the inital values, as by design they are being fed only with 
// changed values.
elmApp.ports.somePort.send(null);

It's a little hackish, but still allows one to use the start-app module.

@joeandaverde

This comment has been minimized.

Show comment
Hide comment
@joeandaverde

joeandaverde Dec 21, 2015

@evancz Is this what you were referring to as far as bringing the value in instead of signal?

https://github.com/evancz/elm-todomvc/blob/1be0712d3fa8bf786bde84d8db5f685e23413876/Todo.elm#L330

For future reference press "y" to toggle the url you copy to contain the git commit hash so links don't break.

joeandaverde commented Dec 21, 2015

@evancz Is this what you were referring to as far as bringing the value in instead of signal?

https://github.com/evancz/elm-todomvc/blob/1be0712d3fa8bf786bde84d8db5f685e23413876/Todo.elm#L330

For future reference press "y" to toggle the url you copy to contain the git commit hash so links don't break.

@mietek

This comment has been minimized.

Show comment
Hide comment
@mietek

mietek Dec 31, 2015

Just ran into this issue as well. The improved documentation for foldp seems to have disappeared, and I’m having a hard time figuring out how to do what I’d like to do — namely, how to get a signal’s initial value over to the JavaScript side.

mietek commented Dec 31, 2015

Just ran into this issue as well. The improved documentation for foldp seems to have disappeared, and I’m having a hard time figuring out how to do what I’d like to do — namely, how to get a signal’s initial value over to the JavaScript side.

@mietek

This comment has been minimized.

Show comment
Hide comment
@mietek

mietek Dec 31, 2015

I couldn’t figure out how to use foldp' and get the behaviour I wanted. Eventually, I went back to StartApp, and settled on sending myself a dummy action as an initial task:

type alias Model =
    { entropy : Int
    }

defaultModel : Model
defaultModel =
    { entropy = 42
    }

type Action =
      Dummy
    | IncreaseEntropy

port getState : Maybe Model -- Restores state from localStorage

init : (Model, Effects Action)
init =
    ( Maybe.withDefault defaultModel getState
    , Effects.task (Task.succeed Dummy)
    )

app : App Model
app = ...

port setState : Signal Model -- Stores state to localStorage
port setState =
    app.model

port setEntropy : Signal Int
port setEntropy =
    Signal.map .entropy app.model

mietek commented Dec 31, 2015

I couldn’t figure out how to use foldp' and get the behaviour I wanted. Eventually, I went back to StartApp, and settled on sending myself a dummy action as an initial task:

type alias Model =
    { entropy : Int
    }

defaultModel : Model
defaultModel =
    { entropy = 42
    }

type Action =
      Dummy
    | IncreaseEntropy

port getState : Maybe Model -- Restores state from localStorage

init : (Model, Effects Action)
init =
    ( Maybe.withDefault defaultModel getState
    , Effects.task (Task.succeed Dummy)
    )

app : App Model
app = ...

port setState : Signal Model -- Stores state to localStorage
port setState =
    app.model

port setEntropy : Signal Int
port setEntropy =
    Signal.map .entropy app.model
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment