Created
August 15, 2015 04:09
-
-
Save dela3499/ca95842fd12a640ed7be to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import StartApp.Simple exposing (start) | |
import Html exposing (..) | |
import Html.Events exposing (onClick, on, targetValue) | |
import String | |
{-- | |
Modified from: https://gist.github.com/bbugh/bf150a8be595068de88c | |
As I undestand it, the spec for this calculator is: | |
- If I input a valid integer and click an operator like Multiply, | |
update the accumulator and show the result. | |
- If my input can't be turned into an integer, | |
don't change the accumulator, but show an error message. | |
------------------------------------------------------------- | |
Here's one attempt to make this work in a clear, DRY way. | |
I've changed the model, so that it contains: | |
- inputString: the raw user input as a string | |
- accumulator: this is an integer as before | |
- output: this is a string that gets shown to the user. It's either | |
the string version of the calculation result, or an error message. | |
When the user updates the input, this triggers the UpdateInputString action. | |
The update function replaces model.inputString with the string provided | |
by the action. At this point, there's no attempt to convert the string | |
into an integer. | |
The interesting part comes when the user triggers the Multiply action | |
(or Divide, or any other operator). This executes handleOperatorAction, | |
which takes a model and a two-argument function like (*), which is the | |
prefix version of the multiply function. | |
Once inside handleOperatorAction, the inputString is finally converted to a | |
Result Int. If the inputString can be successfully converted to an integer, | |
then the accumulator is updated with the result of the appropriate computation, | |
and the output is updated with the string version of this result. If the | |
conversion is not possible, then the accumulator is left unchanged, and | |
the output message is the error message from Err. | |
Lastly, the view function has been updated. I've removed the extra div for the | |
error message, since both the error message and valid result are both shown | |
by model.output. Also, since model.output is a string, rather than and integer, | |
it doesn't need to be converted at this stage. | |
--} | |
type alias Model = | |
{ | |
inputString: String, | |
accumulator: Int, | |
output: String | |
} | |
initialModel : Model | |
initialModel = | |
{ | |
inputString = "0", | |
accumulator = 3, | |
output = "3" | |
} | |
type Action | |
= Multiply | |
| Divide | |
| UpdateInputString String | |
update: Action -> Model -> Model | |
update action model = | |
case action of | |
Multiply -> | |
handleOperatorAction model (*) | |
Divide -> | |
handleOperatorAction model (//) | |
UpdateInputString str -> | |
{ model | inputString <- str} | |
handleOperatorAction: Model -> (Int -> Int -> Int) -> Model | |
handleOperatorAction model operatorFunction = | |
case (String.toInt model.inputString) of | |
Ok value -> | |
let result = operatorFunction model.accumulator value | |
in { model | |
| accumulator <- result | |
, output <- toString result } | |
Err msg -> | |
{ model | output <- msg } | |
onTextInput : Signal.Address a -> (String -> a) -> Attribute | |
onTextInput address f = | |
on "input" targetValue (\v -> Signal.message address (f v)) | |
view : Signal.Address Action -> Model -> Html | |
view address model = | |
div [] [ | |
input [ onTextInput address UpdateInputString ] [ ], | |
button [ onClick address Multiply ] [ text "*" ], | |
text model.output | |
] | |
main : Signal Html.Html | |
main = | |
start { model = initialModel, update = update, view = view } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment