I got this homework assignment (Couldn't add the homework tag). This is just a training problem, not part of a hand-in. I made a recursive function "eval" that evaluates common operations on integers (+, -, *, /) So far, the function can evaluate addition, multiplication and subtraction.
type expr =
| Const of int
| Add of expr * expr
| Mul of expr * expr
| Sub of expr * expr
let rec eval (n: expr): int =
match n with
| Const n -> n
| Add (a, b) -> eval a + eval b
| Mul (a, b) -> eval a * eval b
| Sub (a, b) -> eval a - eval b
This function works perfectly. Next I have to extend the expr type, to allow for division, by adding:
| Div of expr * expr
Also I need to deal with the case of zero division, by making a new type:
('a, 'b) result
Which is the new return type of eval (This is in the assignment description, so i have to do that). This is my attempt:
type ('a, 'b) result =
| Ok of int
| Err of string
let rec eval (n: expr): ('a, 'b)result =
match n with
| Const n -> Ok(Const n)
| Add (a, b) -> Ok(eval a + eval b)
| Mul (a, b) -> OK(eval a * eval b)
| Sub (a, b) -> Ok(eval a - eval b)
| Div (_, Const 0) -> Err "zero division"
| Div (a, b) -> Ok(eval a / eval b)
But I get an error saying that the operators +, -, * and / isn't defined for type ('a, 'b) result, which makes sense. So my question is, is there a way for a new type to inherit from other types? I can't type something like:
type ('a, 'b) result =
| int
| Err of string
Why is that not a thing? Do I have to specify some keyword so be associated with int, if I use int in a new type? f# wants the same output type, no matter the input, is there a way around this? Could I tell the function that the output is either an integer or a string, if I didn't create the new type?
Short answer is no. Like you said F# only allows one return type.
In this case your result
type has 2 possible set of values, the Ok
values and the Error
values.
Your code needs to consider both posibilites, you cannot ignore one.
Remember eval a
does not return an int
anymore, now it returns a result
, which may or may not have an int
inside it.
After you eval a
and eval b
but before you call +
, -
, *
or /
, you need to check if either result is an Error
. Only if both are Ok
can you then apply the operator. If any of them is an Error
you have to return the error, right?
You can use match
to check (and to extract the int
), for instance:
match eval a , eval b with
| Ok a', Ok b' -> Ok(a' + b')
| Error e , _
| _ , Error e -> Error e
Remember you do not need to copy the instruction above 4 times.
You can create a function and pass the operator as a parameter
using this syntax: (+)
, (-)
, (*)
, (/)
.
Another tip: you are considering only the case of division by Const 0
.
What happens if you are dividing by a calculated 0
, for instance 5/(1 - 1)
?
In Functional Programming there is another way of dealing with this.
What you need in the end is a Bind
function (or Apply
).
A good resource to learn about it is this site: https://fsharpforfunandprofit.com/rop/