I'm trying to do some mathematical operations with decimal options
on a custom type:
type LineItem = {Cost: decimal option; Price: decimal option; Qty: decimal option}
let discount = 0.25M
let createItem (c, p, q) =
{Cost = c; Price = p; Qty = q}
let items =
[
(Some 1M , None , Some 1M)
(Some 3M , Some 2.0M , None)
(Some 5M , Some 3.0M , Some 5M)
(None , Some 1.0M , Some 2M)
(Some 11M , Some 2.0M , None)
]
|> List.map createItem
I can do some very simple arithmetic with
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x))
which gives me
val it : seq<decimal option> =
seq [null; Some 0.500M; Some 0.750M; Some 0.250M; ...]
If I try to actually calculate the thing I need
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - (line.Cost
|> Option.map (fun x -> x)))
|> Option.map (fun x -> x * (line.Qty
|> Option.map (fun x -> x))))
I get the error
error FS0001: Type constraint mismatch. The type
'a option
is not compatible with type
decimal
The type ''a option' is not compatible with the type 'decimal'
where I would have expected a seq<decimal option>
.
I must be missing something but I can't seem to spot whatever it is I'm missing.
You are mixing decimal
with decimal option
.
If you're trying to solve everything with Option.map
you may want to try with Option.bind
instead, so your code will be 'linearly nested':
items
|> Seq.map (
fun line ->
Option.bind(fun price ->
Option.bind(fun cost ->
Option.bind(fun qty ->
Some ((discount * price - cost ) * qty)) line.Qty) line.Cost) line.Price)
which can be an interesting exercise, especially if you want to understand monads, then you will be able to use a computation expression, you can create your own or use one from a library like F#x or F#+:
open FSharpPlus.Builders
items |> Seq.map (fun line ->
monad {
let! price = line.Price
let! cost = line.Cost
let! qty = line.Qty
return ((discount * price - cost ) * qty)
}
)
but if you link F#+ you'll have Applicative Math Operators available:
open FSharpPlus.Operators.ApplicativeMath
items |> Seq.map (fun line -> ((discount *| line.Price) |-| line.Cost ) |*| line.Qty)
That's nice stuff to learn but otherwise I would suggest to use F# built-in features instead, like pattern-match, it would be easier:
items
|> Seq.map (fun line -> match line.Price, line.Qty, line.Cost with
| Some price, Some qty, Some cost ->
Some ((discount * price - cost ) * qty)
| _ -> None)
Then since you can also pattern-match over records it can be further reduced to:
items
|> Seq.map (function
| {Cost = Some cost; Price = Some price; Qty = Some qty} ->
Some ((discount * price - cost ) * qty)
| _ -> None)
Note that Option.map (fun x -> x)
doesn't transform anything.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With