Skip to content

Instantly share code, notes, and snippets.

@tvoklov
Created May 14, 2022 23:58
Show Gist options
  • Save tvoklov/38437df4f4a5cf36cf58570fe02ea16a to your computer and use it in GitHub Desktop.
Save tvoklov/38437df4f4a5cf36cf58570fe02ea16a to your computer and use it in GitHub Desktop.
a pretty basic elm calculator i wrote because the one windows has was too slow for me
module InstantCalc exposing (..)
import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
main : Program () Model Msg
main =
Browser.sandbox { init = init, view = view, update = update }
type alias Model =
{ operationType : Operation
, left : String
, right : String
, mResult : Maybe Float
}
type Msg
= SetOperation Operation
| InputL String
| InputR String
| Swap
| SetResultAsLeft
| SetResultAsRight
| Clear
type Operation
= Add
| Subtract
| Divide
| Multiply
| Exponent
calculate : Operation -> Float -> Float -> Float
calculate opType left right =
case opType of
Add ->
left + right
Subtract ->
left - right
Multiply ->
left * right
Divide ->
left / right
Exponent ->
left ^ right
init : Model
init =
Model Add "0" "0" (Just 0.0)
view : Model -> Html Msg
view model =
let
mLeft =
toFloat model.left
mRight =
toFloat model.right
colorLeft =
colorFunc mLeft
colorRight =
colorFunc mRight
resGood =
mFold (\_ -> True) False model.mResult
( phLeft, phRight ) =
placeholderFunc model.operationType
in
div [ style "display" "flex", style "align-items" "center", style "justify-content" "center", style "gap" "1em", style "margin" "1em" ]
[ input [ onInput InputL, value model.left, style "color" colorLeft, style "border-color" colorLeft, style "text-align" "center", placeholder phLeft ] []
, div [ style "display" "flex", style "flex-direction" "column", style "align-content" "stretch", style "gap" "0.75em" ]
[ input [ type_ "button", name "clear", value "clear", onClick Clear ] []
, div []
[ input [ type_ "radio", name "op_type", value "+", checked (model.operationType == Add), onClick (SetOperation Add) ] []
, label [ for "+" ] [ text " + " ]
, input [ type_ "radio", name "op_type", value "-", checked (model.operationType == Subtract), onClick (SetOperation Subtract) ] []
, label [ for "-" ] [ text " - " ]
]
, div []
[ input [ type_ "radio", name "op_type", value "*", checked (model.operationType == Multiply), onClick (SetOperation Multiply) ] []
, label [ for "*" ] [ text " * " ]
, input [ type_ "radio", name "op_type", value "/", checked (model.operationType == Divide), onClick (SetOperation Divide) ] []
, label [ for "/" ] [ text " / " ]
]
, div []
[ input [ type_ "radio", name "op_type", value "^", checked (model.operationType == Exponent), onClick (SetOperation Exponent) ] []
, label [ for "^" ] [ text " ^ " ]
]
, input [ type_ "button", name "swap", value "swap", onClick Swap ] []
]
, input [ onInput InputR, value model.right, style "color" colorRight, style "border-color" colorRight, style "text-align" "center", placeholder phRight ] []
, p [] [ text " = " ]
, p [] [ text (mFold String.fromFloat "???" model.mResult) ]
, div [ style "display" "flex", style "flex-direction" "column", style "gap" "0.1em" ]
[ text "Set as:"
, input [ type_ "button", disabled (not resGood), name "setAsLeft", value phLeft, onClick SetResultAsLeft ] []
, input [ type_ "button", disabled (not resGood), name "setAsRight", value phRight, onClick SetResultAsRight ] []
]
]
placeholderFunc : Operation -> ( String, String )
placeholderFunc op =
case op of
Add ->
( "Left Addend", "Right Addend" )
Subtract ->
( "Minuend", "Subtrahend" )
Divide ->
( "Dividend", "Divider" )
Multiply ->
( "Left Factor", "Right Factor" )
Exponent ->
( "Base", "Exponent" )
colorFunc : Maybe Float -> String
colorFunc f =
case f of
Just _ ->
"black"
Nothing ->
"red"
mZip : Maybe a -> Maybe b -> Maybe ( a, b )
mZip a b =
Maybe.andThen (\r -> Maybe.map (\l -> ( l, r )) a) b
mFold : (a -> b) -> b -> Maybe a -> b
mFold f z m =
case m of
Just a ->
f a
Nothing ->
z
update : Msg -> Model -> Model
update msg model =
case msg of
SetOperation op ->
let
newModel =
{ model | operationType = op }
in
{ newModel | mResult = calculateModel newModel }
InputL l ->
let
newModel =
{ model | left = l }
in
{ newModel | mResult = calculateModel newModel }
InputR r ->
let
newModel =
{ model | right = r }
in
{ newModel | mResult = calculateModel newModel }
Swap ->
let
newModel =
{ model | right = model.left, left = model.right }
in
{ newModel | mResult = calculateModel newModel }
SetResultAsLeft ->
let
mNewModel =
Maybe.map (\res -> { model | left = String.fromFloat res }) model.mResult
in
Maybe.map (\m -> { m | mResult = calculateModel m }) mNewModel |> Maybe.withDefault model
SetResultAsRight ->
let
mNewModel =
Maybe.map (\res -> { model | right = String.fromFloat res }) model.mResult
in
Maybe.map (\m -> { m | mResult = calculateModel m }) mNewModel |> Maybe.withDefault model
Clear ->
{ model | left = "0", right = "0", mResult = Just 0 }
calculateModel : Model -> Maybe Float
calculateModel model =
mZip (toFloat model.left) (toFloat model.right)
|> Maybe.map (\( l, r ) -> calculate model.operationType l r)
toFloat : String -> Maybe Float
toFloat s =
String.replace "," "." s |> String.toFloat
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment