Arithmetic with decimal option types - f#

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.

One problem you have is the following code:
(line.Cost |> Option.map (fun x -> x))
The lambda function (fun x -> x) already exists. This is the id function. It just returns whatever you have unchanged. You also could write the code you have like this:
(line.Cost |> Option.map id)
And the next thing. Mapping over the id function makes no sense. You unwrap whatever is inside the option, apply the id function to it. What didn't change the decimal at all. Then you wrap the decimal again in an option. You also can just write:
line.Cost
and completely remove the Option.map as it does nothing.
So the code you have here:
|> Option.map (fun x -> x - (line.Cost |> Option.map (fun x -> x)))
is identical to:
|> Option.map (fun x -> x - line.Cost)
This obviously does not work because here you try to subtract x a decimal with line.Cost a option decimal. So you get a type-error.
I guess what you really want to do is to subtract line.Cost from line.Price if line.Cost is present, otherwise you want to preserve line.Price unchanged.
One way would be just to provide a default value for line.Costs that can be used and have no effect on the subtraction. For example you could use the value 0 for subtraction if line.Costs is None.
So you also could write something like this instead:
|> Option.map (fun x -> x - (defaultArg line.Cost 0m))
A default value for multiplication would be 1m. So you overall end with.
items
|> Seq.map (fun line ->
line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - (defaultArg line.Cost 0m))
|> Option.map (fun x -> x * (defaultArg line.Qty 1m)))
The above code for example returns:
[None; Some -2.500M; Some -21.250M; Some 0.500M; Some -10.500M]
If your goal is that a whole computation turns into None as soon a single
value is None. I would just add map2 as a helper function.
module Option =
let map2 f x y =
match x,y with
| Some x, Some y -> Some (f x y)
| _ -> None
then you just can write:
items
|> List.map (fun line ->
line.Price
|> Option.map (fun price -> price * discount)
|> Option.map2 (fun cost price -> price - cost) line.Cost
|> Option.map2 (fun qty price -> price * qty) line.Qty)
and it will return:
[None; None; Some -21.250M; None; None]

Within the Option.map the x is actually a decimal, but the signature for Option.map is 'T option -> 'U option. So here:
Option.map (fun x -> x - (line.Cost |> Option.map (fun x -> x)))
you have the following:
Option.map (fun x -> /*decimal*/ x - /*decimal option*/(line.Cost |> Option.map (fun x -> x)))
So the decimal option has to be converted to decimal to be compatible with what's in the first Option.map. But now you have to deal with None result.
Below a quick (and nasty) fix that just is using an if statement to extract Value (which will be a decimal) or if None then return 0.
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - if line.Cost.IsSome then line.Cost.Value else 0m)
|> Option.map (fun x -> x * if line.Qty.IsSome then line.Qty.Value else 0m))
For a more sophisticated solution I recommend this answer.

For completeness sake, you may also leverage the monadic properties of the option type by "lifting" the values outside of the option. This is a somewhat simpler variant of the applicative approach linked by #PiotrWolkowski and those shown by #Gustavo. Applicatives not only wrap values in the monad, but also the functions applied to them.
We start by making the option type amenable to monadic operations in terms of bind and return. Thankfully, those functions are already defined, there's only a little tweak in the argument order.
let (>>=) ma f = Option.bind f ma
let ``return`` = Some
On top of this there's the lift function and a couple of operators for convenience. If needed, they could be generalized by marking them inline.
let liftOpt op x y =
x >>= fun a ->
y >>= fun b ->
``return`` (op a b)
let (.*) = liftOpt (*)
let (.-) = liftOpt (-)
Now your calculation becomes
items
|> Seq.map (fun line ->
(line.Price .* Some discount .- line.Cost) .* line.Qty )
|> Seq.iter (printfn "%A")
which will print
<null>
<null>
Some -21.250M
<null>
<null>

Related

F#: Can the match expression not return sequences when mapped to from non sequence type?

I am trying to create a recursive function that is conditionally calls itself and so far is is defined as follows:
let rec crawlPage (page : String, nestingLevel : int) =
HtmlDocument.Load(page)
|> fun m -> m.CssSelect("a")
|> List.map(fun a -> a.AttributeValue("href"))
|> Seq.distinctBy id
|> Seq.map (fun x -> baseUrl + x)
|> Seq.map (fun x ->
match nestingLevel with
// Compiler says it is expecting a but given seq<a> in reference to the recursive call
| _ when (nestingLevel > 0) -> crawlPage(x, (nestingLevel - 1))
| _ when (nestingLevel <= 0) -> ignore
| _ -> (* To silence warnigs.*) ignore
)
It is that the Seq.map (fun x -> ...) cannot handle the return sequence or can the match condition not handle the returned sequence? Given that the crawlPage is underlined by the compiler it seems that the match statement cannot handle the seq returned so how can this be done?
The rule is that all the matching branches must return the same type, so you have to:
Replace ignore with Seq.singleton x to indicate that this branch yields nothing more except the x itself.
At the end, concat (flat map) the seq<seq<string>> to transform it to a seq<string>.
The code would be:
|> Seq.map (fun x ->
match nestingLevel with
| _ when (nestingLevel > 0) -> crawlPage(x, (nestingLevel - 1))
| _ -> Seq.singleton x)
|> Seq.concat
The existing post answers your specific question, but I think it is worth noting that there are a few other changes that could be done to your code snippet. Some of those are a matter of personal preference, but I believe they make your code simpler:
You can use sequence comprehension, which lets you handle recursive calls nicely using yield! (and non-recursive using yield)
You do not actually need match, because you have just two branches that are more easily tested using ordinary if
I would also avoid the |> fun m -> m.Xyz pattern, because it's not necessary here.
With all those tweaks, my preferred version of the code snippet would be:
let rec crawlPage (page : String, nestingLevel : int) = seq {
let urls =
HtmlDocument.Load(page).CssSelect("a")
|> List.map(fun a -> a.AttributeValue("href"))
|> Seq.distinctBy id
|> Seq.map (fun x -> baseUrl + x)
for x in urls do
if nestingLevel > 0 then
yield! crawlPage(x, (nestingLevel - 1))
else
yield x }

Better way to sum rows

I'm learning F# (again) and I'm trying to sum some rows in excel. This is my attempt.
let sumRows (source: seq<double[]>) =
source
|> Seq.reduce (fun a b -> (a, b) ||> Seq.map2 (fun x y -> x + y) |> Seq.toArray)
Can it be done better? I already discovered double forward pipe operator, but now, whole part fun a b -> (a, b) ||> seems quite redundant...
You are right it is redundant, because the double pipe operator is convenient only when you need to convert a tuple into two separate parameters. In this case you already have them as 2 separate parameters so you could just pass them like this:
let sumRows (source: seq<double[]>) =
source
|> Seq.reduce (fun a b -> Seq.map2 (fun x y -> x + y) a b |> Seq.toArray)
We can get rid of the |> Seq.toArray by replacing Seq.map2 with Array.map2:
let sumRows (source: seq<double[]>) =
source
|> Seq.reduce (fun a b -> Array.map2 (fun x y -> x + y) a b)
now we can simplify further by removing the fun a b ->:
let sumRows (source: seq<double[]>) =
source
|> Seq.reduce (Array.map2 (fun x y -> x + y) )
Finally, did you know that an operator like + can be treated as a 2 parameters function
by putting in parenthesis (+)?
let sumRows2 (source: seq<double[]>) =
source
|> Seq.reduce (Array.map2 (+) )
All of this versions have the same signature and they are all correct. You choose whichever suits more your style.
BTW, you maybe tempted to go one further and do this:
let sumRows2 = Seq.reduce (Array.map2 (+) )
but it causes issues with the famous Value restriction error. There are workarounds like adding the type annotation or actually using it somewhere in the code, but the best workaround is to add the parameter, like we had before.

Is it correct that Deedle/Series is slow compared to a list?

I am working on a data "intensive" app and I am not sure if I should use Series./DataFrame. It seems very interesting but it looks also way slower than the equivalent done with a List ... but I may not use the Series properly when I filter.
Please let me know what you think.
Thanks
type TSPoint<'a> =
{
Date : System.DateTime
Value : 'a
}
type TimeSerie<'a> = TSPoint<'a> list
let sd = System.DateTime(1950, 2, 1)
let tsd =[1..100000] |> List.map (fun x -> sd.AddDays(float x))
// creating a List of TSPoint
let tsList = tsd |> List.map (fun x -> {Date = x ; Value = 1.})
// creating the same as a serie
let tsSeries = Series(tsd , [1..100000] |> List.map (fun _ -> 1.))
// function to "randomise" the list of dates
let shuffleG xs = xs |> List.sortBy (fun _ -> Guid.NewGuid())
// new date list to search within out tsList and tsSeries
let d = tsd |> shuffleG |> List.take 1000
// Filter
d |> List.map (fun x -> (tsList |> List.filter (fun y -> y.Date = x)))
d |> List.map (fun x -> (tsSeries |> Series.filter (fun key _ -> key = x)))
Here is what I get:
List -> Real: 00:00:04.780, CPU: 00:00:04.508, GC gen0: 917, gen1: 2, gen2: 1
Series -> Real: 00:00:54.386, CPU: 00:00:49.311, GC gen0: 944, gen1: 7, gen2: 3
In general, Deedle series and data frames do have some extra overhead over writing hand-crafted code using whatever is the most efficient data structure for a given problem. The overhead is small for some operations and larger for some operations, so it depends on what you want to do and how you use Deedle.
If you use Deedle in a way in which it was intended to be used, then you'll get a good performance, but if you run a large number of operations that are not particularly efficient, you may get a bad performance.
In your particular case, you are running Series.filter on 1000 series and creating a new series (which is what happens behind the scenes here) does have some overhead.
However, what your code really does is that you are using Series.filter to find a value with a specific key. Deedle provides a key-based lookup operation for this (and it's one of the things it has been optimized for).
If you rewrite the code as follows, you'll get much better performance with Deedle than with list:
d |> List.map (fun x -> tsSeries.[x])
// 0.001 seconds
d |> List.map (fun x -> (tsSeries |> Series.filter (fun key _ -> key = x)))
// 3.46 seconds
d |> List.map (fun x -> (tsList |> List.filter (fun y -> y.Date = x)))
// 40.5 seconds

Converting None to 0 (F#)

This expression correctly evaluates to 225.58:
let alfki = ctx.``[dbo].[Customers]``.Individuals.ALFKI
let changeNoneToZero x = match x with | Some v -> v | None -> 0m
let freights = alfki.FK_Orders_Customers |> Seq.map (fun x -> changeNoneToZero x.Freight) |> Seq.sum
corresponding to the following database query:
select SUM(freight)
from dbo.Orders
where CustomerID = (
select CustomerID from Customers
where CustomerID = 'ALFKI')
Is there some built in equivalent or close analogue of the changeNoneToZero function? Could I be seeking to Seq.sumBy something instead?
#ildjarn's approach of using Seq.choose works. Instead of converting None to 0m and then summing everything, the following selects only those values that are not None and pipes them into Seq.sum:
let freights = alfki.FK_Orders_Customers
|> Seq.choose (fun x -> x.Freight)
|> Seq.sum
Thanks #ildjarn!
You can also use Option.fold:
alfki.FK_Orders_Customers |> Option.fold (fun _ a -> a) 0m
And you can make it generic:
let inline valOrZero ov = ov |> Option.fold (fun _ a -> a) LanguagePrimitives.GenericZero

Why does the pipe operator work?

If the pipe operator is created like this:
let (|>) f g = g f
And used like this:
let result = [2;4;6] |> List.map (fun x -> x * x * x)
Then what it seems to do is take List.Map and puts it behind (fun x -> x * x * x)
And doesn't change anything about the position of [2;4;6]
So now it looks like this:
let result2 = [2;4;6] (fun x -> x * x * x) List.map
However this doesn't work.
I am just learning f# for the first time now. And this bothered me while reading a book about f#. So I might learn what I'm missing later but I decided to ask anyway.
It is obvious though that I am missing something major. Since I can easily recreate the pipe operator. But I don't get why it works. I might embarrass myself very soon as I learn more. Oh well.
The pipe operator is simply syntactic sugar for chained method calls. It's very similar to how linq expressions are expressed in C#.
Explanation from here:
Forward Pipe Operator
I love this guy. The Forward pipe operator is simply defined as:
let (|>) x f = f x
And has a type signature:
'a -> ('a -> 'b) -> 'b
Which translates to: given a generic type 'a, and a function which takes an 'a and returns a 'b, then return the application of the function on the input.
Rather than explaining this, let me give you an example of where it can be used:
// Take a number, square it, then convert it to a string, then reverse that string
let square x = x * x
let toStr (x : int) = x.ToString()
let rev (x : string) = new String(Array.rev (x.ToCharArray()))
// 512 -> 1024 -> "1024" -> "4201"
let result = rev (toStr (square 512))
The code is very straight forward, but notice just how unruly the syntax looks. All we want to do is take the result of one computation and pass that to the next computation. We could rewrite it by introducing a series of new variables:
let step1 = square 512
let step2 = toStr step1
let step3 = rev step2
let result = step3
But now you need to keep all those temporary variables straight. What the (|>) operator does is take a value, and 'forward it' to a function, essentially allowing you to specify the parameter of a function before the function call. This dramatically simplifies F# code by allowing you to pipe functions together, where the result of one is passed into the next. So to use the same example the code can be written clearly as:
let result = 512 |> square |> toStr |> rev
Edit:
In F# what you're really doing with a method call is taking a function and then applying it to the parameter that follows, so in your example it would be List.map (fun x -> x * x * x) is applied to [2;4;6]. All that the pipe operator does is take the parameters in reverse order and then do the application reversing them back.
function: List.map (fun x -> x * x * x)
parameter: [2;4;6]
Standard F# call syntax: f g
Reversed F# call syntax: g f
Standard:
let var = List.map (fun x -> x * x * x) [2;4;6]
Reversed:
let var = [2;4;6] |> List.map (fun x -> x * x * x)
The brackets around |> mean it is an infix operator so your example could be written
let result = (|>) [2;4;6] (List.map (fun x -> x * x * x))
Since |> applies its first argument to the second, this is equivalent to
let result = (List.map (fun x -> x * x)) [2;4;6]
As others have said above, basically you're misunderstanding what result2 would resolve to. It would actually resolve to
List.map (fun x -> x * x * x) [2;4;6]
List.map takes two arguments: a function to apply to all elements in a list and a list. (fun x -> x * x * x) is the first argument and [2;4;6] is the second.
Basically just put what's on the left of |> after the end of what's on the right.
If you enter your definition of |> into fsi and look at the operator's signature derived by type inference you'll notice val ( |> ) : 'a -> ('a -> 'b) -> 'b, i.e. argument 'a being given to function ('a -> 'b) yields 'b.
Now project this signature onto your expression [2;4;6] |> List.map (fun x -> x * x * x) and you'll get List.map (fun x -> x * x * x) [2;4;6], where the argument is list [2;4;6] and the function is partially applied function of one argument List.map (fun x -> x * x * x).

Resources