2
\$\begingroup\$

I am new to functional programming and I decided to learn it thru Elm instead of haskell. I wrote a practice code where there are 2 buttons and their names correspond to the number of times they are clicked. The following is my implementation:

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)

main = Html.beginnerProgram {model=model, view=view, update=update}

-- model
type alias Model = 
   { a : Int
   , b : Int
   , total : Int}

model : Model
model = Model 0 0 0

 -- view
type Msg = Add Foo
type Foo = A | B

view : Model -> Html Msg
view model =
   div [] 
     [ button [onClick (Add A)] [text (toString model.a)]
     , button [onClick (Add B)] [text (toString model.b)]
     , div [] [ text (toString model.total)]
     ]


 -- update
update : Msg -> Model -> Model
update msg model =
   case msg of
     Add foo ->
       case foo of
         A ->
           let
             a = model.a + 1
             total = a + model.b
           in
             {model | a = a, total = total}
         B ->
           let
             b = model.b + 1
             total = b + model.a
           in
             {model | b = b, total = total}

I feel like that the above code is a dumb implementation of what I wanted to accomplish. Can somebody suggest a better implementation?

\$\endgroup\$

migrated from stackoverflow.com Nov 21 '17 at 18:26

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ I recommend you configure your editor to format-on-save using elm-format \$\endgroup\$ – user633183 Nov 21 '17 at 16:22
  • \$\begingroup\$ Thank you. I just learned how to do that. BTW, any idea why elm-format uses 4-spaces indentation? It's a bit too much for me. I think 2 spaces are enough. \$\endgroup\$ – Joseph Nov 22 '17 at 10:12
4
\$\begingroup\$

Here are a few things I'd recommend changing:

  1. There's no need to store the sum of a and b. You can always compute it in the view with a + b. This simplifies the update function a lot.

  2. You can match Foo A and Foo B in update instead of a nested case.

  3. Optionally, you can destructure model into {a, b} in view. I don't have any opinion on this. Either way is fine.

Here's the final elm-format'd code:

module Main exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)


main =
    Html.beginnerProgram { model = model, view = view, update = update }


type alias Model =
    { a : Int
    , b : Int
    }


model : Model
model =
    Model 0 0


type Msg
    = Add Foo


type Foo
    = A
    | B


view : Model -> Html Msg
view { a, b } =
    div []
        [ button [ onClick (Add A) ] [ text (toString a) ]
        , button [ onClick (Add B) ] [ text (toString b) ]
        , div [] [ text (toString (a + b)) ]
        ]


update : Msg -> Model -> Model
update msg model =
    case msg of
        Add A ->
            { model | a = model.a + 1 }

        Add B ->
            { model | b = model.b + 1 }

Live: https://ellie-app.com/c7GLFrXx3a1/0

\$\endgroup\$
  • 1
    \$\begingroup\$ Wow, this greatly simplified the code. Many thanks for pointing out number (2). Your answer is greatly appreciated! \$\endgroup\$ – Joseph Nov 21 '17 at 23:35
  • 1
    \$\begingroup\$ There's one more change I'd personally make: use <| to reduce parentheses: text (toString a) -> text <| toString a and similar for the two lines below. \$\endgroup\$ – Dogbert Nov 22 '17 at 4:17
  • \$\begingroup\$ Ooh, that's nice. I'll practice on using that operator to reduce my use of parentheses. \$\endgroup\$ – Joseph Nov 22 '17 at 10:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.