Hyperapp: The Elm Architecture 馃嵉

Created on 8 Feb 2017  路  15Comments  路  Source: jorgebucaran/hyperapp

Maybe it would be better if effects shouldn't be called directly by user but go along with result of updating model as a second element in an array or some other structure and that second element should be handled by runtime so it will be look more like TEA:

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    MorePlease ->
      (model, getRandomGif model.topic)

    NewGif (Ok newUrl) ->
      ( { model | gifUrl = newUrl }, Cmd.none)

    NewGif (Err _) ->
      (model, Cmd.none)
Discussion

All 15 comments

Do you have a more complex example where you're actually updating the model before firing off the effect? It would be helpful to illustrate some benefits.

I'm not a big fan of that aspect of Elm, to be honest. It creates a lot of that Cmd.none boilerplate, like in your example (or the shorthand version with !). I'm sure elm had a very good reason to do it that way (whether it be a type system requirement or effect management reason). elm is a fantastic language, but understanding how to chain updates & effects isn't very simple.

With javascript's flexility I'm liking how hyperapp just lets you call the reducers and effects the same way.

@SkaterDad often when I need to request something I need to set loading flag or reset some field to initial state

The example in the readme implies that's already possible, since you can call the update functions from within the effect?

const effects = {
    waitThenAdd: (model, msg) => {
        msg.toggle()
        wait(1000).then(msg.add).then(msg.toggle)
    }
}

EDIT: I hope to start migrating an app sometime this week, and definitely plan on doing things like that also. Hopefully it goes smoothly.

@SkaterDad I didn't said that you can't do this in current implementation I just pointed that you have to call this side-effect things directly and call update methods inside these effects which I think looks not that good in contrast with elm

Fair enough :smile:

This is the fun part of open source to me, learning the different ways people prefer to do things.

@itrelease 馃 What would be the proposed syntax using JavaScript?

@jbucaran

update: {
  loadMore: (model, data, effects) {
    return {
      model: { ...model, loading: true },
      effect: effects.fetchMore(data.url) // but this shouldn't start fetching immediately this should be more like Task thing that will be forked and run by runtime
  };
}

@itrelease Hmm, this is getting interesting!

I'll need a more concrete example, if possible, based in one of the existing ones of how both models would contrast.

There's an example in the documentation for effects.

How would that entire example look in your model?

If it isn't reproducible, then you can come up with a new example yourself, but please make sure to show both an implementation using HyperApp and one in pseudo-code in your model/idea.

@jbucaran

const model = {
  counter: 0,
  waiting: false,
  error: null
}

const view = (model, msg) => {
  return html`
    <button
      onclick=${msg.waitThenAdd}
      disabled=${model.waiting}
    >
      ${model.counter}
    </button>
  `;
};

const update = {
  waitThenAdd: (model, data, effects) => {
    return {
      model: ({ ...model, waiting: true }),
      effect: effects.wait(
        1000,
        (_) => ({ ...model, waiting: false, error: null, counter: model.counter + 1 }),
        (err) => ({ ...model, waiting: false, error: err })
      )
    };
  }
};

const effects = {
  wait: (time, onReject, onResolve) =>
    (model) => new Promise(resolve => setTimeout(_ => resolve(), time));
}

app({ model, view, update, effects });

@itrelease Thank you for your time and writing this for me. I'm going through it now, trying to understand what's going on.

At first glance I can say, however, it looks more complex than the original example.

I agree that the example now seems a bit more complex. Maybe all we need to do is state in the docs that the effects do not cause a re-render to occur. Essentially, that is the only difference currently between effects and reducers.

What is TEA? The drink? :laughing: Sry, i'm not Elm.

As seeing the examples and discussion, what's the problem to do such thing currently?

What about the following?

const model = {
  counter: 0,
  waiting: false,
  error: null
}

const view = (model, msg) => {
  return html`
    <button
      onclick=${msg.waitThenAdd}
      disabled=${model.waiting}
    >
      ${model.counter}
    </button>
  `;
};

const update = {
  add: (model, data) => (data)
};

const wait = (time) => new Promise(resolve => setTimeout(_ => resolve(), time))

const effects = {
  wait: (model, msg, data) => {
    msg.add({ waiting: true })
    wait(1000)
      .then(() => {
        msg.add({ waiting: false, counter: model.counter + 1 })
      })
      .catch((err) => {
        msg.add({ waiting: false, error: err })
      })
  }
}

app({ model, view, update, effects });

I believe it won't work, but.. believe it is possible to be done too :D Just a matter of thinking.

Oooh, right TEA means ... okey :D

Going to close here as I don't think I'll be changing the effects API.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jscriptcoder picture jscriptcoder  路  4Comments

dmitrykurmanov picture dmitrykurmanov  路  4Comments

jorgebucaran picture jorgebucaran  路  4Comments

jbrodriguez picture jbrodriguez  路  4Comments

jamen picture jamen  路  4Comments