Search code examples
elmelm-signal

How to propagate clicks signal to model update in Elm?


I'm writing a game in Elm and in this game there's a button that when pressed should reset the game board to its initial state. I don't understand however how a button click signal is to be propagated to the board model's update function. In the code below, I've just passed the unit value to stepBoard but I can't do much with it so how can I solve it?

--MODEL
type Board = [(Int,Int)]
type Game = {name: String, board: Board}
defaultGame = {name = "Test", board = [(3,3)]}

--UPDATE
stepBoard click (colsInit, rowsInit) (rows, cols) g = 
          {g | board <- if ? then --some initial value (colsInit, rowsInit)
                        else      --some updated value using (rows, cols)} 
stepGame: Input -> Game -> Game                                      
stepGame {click, name, rowsColsInit, rowsCols} g = stepBoard click (0,0) (0,0) g
game = foldp stepGame defaultGame input

--INPUT
type Input = {click:(), name: String, rowsColsInit: (Int,Int), rowsCols: (Int,Int)}
input = Input <~ clicks ~ nameSignal ~ rowsColsInitSignal ~ rowsColsSignal

--VIEW
(btn, clicks) = Input.button "Click"

Solution

  • Combining foldp and click information can be unintuitive.

    The usual solution employed is an ADT to encode the different possible events:

    data Input = Init (Int, Int)
               | Update { name         : String
                        , rowsCols     : ( Int, Int )
                        }
    input = merge (Init <~ (sampleOn clicks rowsColsInitSignal))
                  (OtherInput <~ nameSignal ~ rowsColsSignal)
    

    merge merges two signals and always creates a constant function.

    Now you can just match by case in your update function:

    stepBoard input g = 
      let newBoard = 
            case input of
              Init (initRows, initCols) -> -- do your initialisation here
              Update {name, rowsCols}   -> -- do your update here
      in { g | board <- newBoard }