3

In F# SAFE stack there is a DU type, which defines all types of messages flowing between server and client. In sample application there are <10 of them, I looked on provided examples - all of them have not many possible types. But what if the application is not small - there will be hundreds of types in DU; which will become hard to maintain. So I decided to divide this type in sub-types and put logic in corresponding files.

Here is my simplified type:

 type Msg2 =
    | LoginMsg of LoginState
    | RegisterMsg of RegisterState
    | ViewUpdateMsg of ViewUpdateState

Login state is defined in another file:

 type LoginState =
        | Login
        | LogInResult of LoginResult

Login module works with logins:

let workWithLogin (model: Model) (msg: LoginState) (todosApi: ITodosApi) : Model * Cmd<LoginState> =
    match msg with
    | LoginState.Login ->
        let result =
            Cmd.OfAsync.perform todosApi.login model.InputData LoginState.LogInResult

        model, result
    | LoginState.LogInResult data ->
        { model with
              LoginState = getVal data.Message
              Token = data.Token },
        Cmd.none

Note that it returns Model * Cmd<LoginState>.

Now I need to fix my update function:

let update2 (msg: Msg2) (model: Model) : Model * Cmd<Msg2> =
     match msg with
     | LoginMsg n ->
         let ret = workWithLogin model n todosApi
         model, snd ret
     | RegisterMsg n -> ...
     | ViewUpdateMsg n ->  ...

The problem here is that I get Cmd<LoginState> from login module and need to convert it to Cmd<Msg2> somehow. enter image description here So I either need to create new Msg2 type or convert DU variant LoginMsg of LoginState to Msg2. I dont understand how to get LogInResult data from Cmd<LoginState>.

How can I fix this? How the problem with many message types is solved in big projects?

Michael Snytko
  • 327
  • 3
  • 13
  • I think what you have done it's in the right direction. If I were you I would I would take a look at [The Elmish Book](https://zaid-ajaj.github.io/the-elmish-book/#/chapters/scaling/splitting-programs), where it talks about larger applications – Federico Rossi Aug 06 '21 at 07:13
  • There are sample applications that demonstrate this in the Elmish.WPF repo on GitHub. I don't know if there are similar demos for Bolero, but possibly. Those are the two Elmish based frameworks I'm familiar with. – Bent Tranberg Aug 06 '21 at 12:07

1 Answers1

2

What you've done with wrapping in "sub"-messages is the right way to go, and you should probably do the same thing with the model - i.e. let Login have it's own model. To convert between different Cmd<'msg> there is a Cmd.map. In your case you can:

let update2 (msg: Msg2) (model: Model) : Model * Cmd<Msg2> =
  match msg with
  | LoginMsg n ->
      let (loginModel, loginCmd) = workWithLogin model n todosApi
      { model with Login = loginModel }, Cmd.map LoginMsg loginCmd

You can see some API description of Cmd here: https://elmish.github.io/elmish/cmd.html

Andreas Ågren
  • 3,879
  • 24
  • 33