# Calculator

20 Mar 2013

## Console Version

How would we write a calculator program in Haskell? A simple calculator should be able to perform the basic arithmetic operations on two numbers.

``````data Operator = Add | Subtract | Multiply | Divide

eval l o r = case o of
Subtract -> l - r
Multiply -> l * r
Divide   -> l / r

prompt txt = do
putStrLn txt

main = do
l <- prompt "Left operand?"
o <- prompt "Which operator?"
r <- prompt "Right operand?"
putStrLn \$ "The result is " ++ show (eval l o r)``````

Note that since we used `deriving Read`, we get an instance of the `Read` typeclass. That means that the `read` function now has a default implementation for our datatype `Operator`. This default implementation expects the input to be formatted in the same way that `show` formats its output. In this case, that means it expects one of "Add", "Subtract", "Multiply", or "Divide".

But something interesting is going on! `prompt "Left operand"` and `prompt "Right operand"` are very similar expressions, but one reads numbers while the other reads operators. The type of read is `Read a => String -> a`. This means that `read` can be used on a `String`, to return any type `a`, that implements the `Read` typeclass.

``````main = do
print (value :: Int)``````

Try changing the type of `value` to `String`, `[Int]`, `Bool`, etc. If you remove it, there will be an "ambiguous type variable" exception - this is the compiler telling you that it needs more information in order to determine the type. Without knowing the type, Haskell can't determine how to `readLn` (which uses `read`) or `print` (which uses `show`).

## Yesod Version

Let's make our calculator program into a website! Here's our pre-requisites:

``````{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses,
import Yesod
import Yesod.Form
import Control.Applicative``````

Nevermind the `LANGUAGE` stuff. Beyond that, we're depending on Yesod and its forms. The operators from `Control.Applicative` are needed to build forms.

Next, we write a simple one-page website:

``````data Calculator = Calculator

instance Yesod Calculator

-- Necessary to tell Yesod how to render form-related error messages.
instance RenderMessage Calculator FormMessage where
renderMessage _ _ = defaultFormMessage

mkYesod "Calculator" [parseRoutes|
/ HomeR GET
|]

main = warpEnv Calculator``````

The code for the form looks like this:

``````data Calculation = Calculation Double Operator Double

form = renderDivs \$ Calculation
<\$> areq doubleField               "Left operand"  Nothing
<*> areq (selectField optionsEnum) "Operator"      Nothing
<*> areq doubleField               "Right operand" Nothing``````

See the Yesod Book for more information about forms. In this example `areq` builds a required field. `doubleField` and `selectField optionsEnum` specify different types of forms widgets (as well as a way of parsing the results).

The operators `(<\$>)` and `(<*>)` from `Control.Applicative` allow us to treat the fields as if they yield values, and wrap up those results using a `Calculation` datatype.

Finally, we can define our page handler and use logic code that's similar to the console version above:

``````{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses,
import Yesod
import Yesod.Form
import Control.Applicative

data Calculator = Calculator

instance Yesod Calculator

instance RenderMessage Calculator FormMessage where
renderMessage _ _ = defaultFormMessage

mkYesod "Calculator" [parseRoutes|
/ HomeR GET
|]

main = warpEnv Calculator

-- show
data Calculation = Calculation Double Operator Double

form = renderDivs \$ Calculation
<\$> areq doubleField               "Left operand"  Nothing
<*> areq (selectField optionsEnum) "Operator"      Nothing
<*> areq doubleField               "Right operand" Nothing

getHomeR :: GHandler sub Calculator RepHtml
getHomeR = do
((result, widget), enctype) <- runFormGet form

let resultText = "Result: " ++ case result of
FormSuccess (Calculation l o r) -> show (eval l o r)
_ -> ""

defaultLayout \$ do
[whamlet|
<h1> Calculator </h1>
<form enctype=#{enctype}>
^{widget}
<input #button type="submit" value="Calculate!">
<h2> #{toHtml resultText}
|]
toWidget [cassius|
body          { width: 15em; margin: 0em auto; }
#button       { width: 10em; }
input, select { width: 4em; margin: 0.5em; }
.required     { text-align: right; }
|]

data Operator = Add | Subtract | Multiply | Divide
deriving (Eq, Enum, Bounded, Show)

eval l o r = case o of
The logic code has one small difference - now, instead of `deriving Read`, we do `deriving (Eq, Enum, Bounded, Show)`. This is because these instances are needed by `selectField optionsEnum`. `Eq` is needed to determine which of the options was selected. `Bounded` and `Enum` are used to enumerate all the possible values of `Operator`. `Show` is used to determine the label used in the drop-down box entry.