Skip to content

Instantly share code, notes, and snippets.

@adinapoli
Created April 1, 2012 10:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adinapoli/2274497 to your computer and use it in GitHub Desktop.
Save adinapoli/2274497 to your computer and use it in GitHub Desktop.
A VERY simple application scenario of the Maybe Monad in F#
open FSharpx.Option
/// Type synonims
type ProductId = string
type Price = float
type Inventory() =
let inv_ = new System.Collections.Generic.Dictionary<ProductId, Price>()
member this.Stock (id : ProductId) (price : Price) =
inv_.Add(id, price)
member this.Price (id : ProductId) =
try
Some(inv_.[id])
with
| :? System.Collections.Generic.KeyNotFoundException -> None
let inline (|@|) (p1 : Price option) (p2 : Price option) =
maybe{
let! v1 = p1
let! v2 = p2
return! Some(v1 + v2)
}
let reporter (priceSum : Price option) : unit =
match priceSum with
| Some(p) -> printfn "Total price: %g." p
| None -> printfn "One or more id not found."
//Initialize a basic inventory and throw inside a bunch of items
let inventory = new Inventory()
inventory.Stock "MyWidget" 10.3
inventory.Stock "Gizmos" 4.34
inventory.Stock "Foo1000" 8.12
//Sum prices
inventory.Price("MyWidget") |@| inventory.Price("Gizmos") |> reporter
//A further step, price sum pipelining
inventory.Price("MyWidget")
|> (|@|) (inventory.Price("Gizmos"))
|> (|@|) (inventory.Price("Foo1000"))
|> reporter
//A failing computation
inventory.Price("MyWidget")
|> (|@|) (inventory.Price("Gizmos"))
|> (|@|) (inventory.Price("DoesNotExist"))
|> reporter
//A completely automatic procedure
let sumAndReport (inventory : Inventory) (ids : ProductId list) : unit =
let basket = List.map (fun pid -> inventory.Price(pid)) ids
in List.reduce (fun p1 p2 -> p1 |@| p2) basket
|> reporter
@mausch
Copy link

mausch commented Apr 1, 2012

Very nice! I forked this with a few suggestions: https://gist.github.com/2276291

  • FSharpx already has a function to search in a dictionary and return an option.
  • Many type annotations are unnecessary: F# is great at inferring types!
  • lift2 instead of computation expression
  • Piping operators looks a bit funky, best to avoid it.

@adinapoli
Copy link
Author

Thanks! I'm preparing a blog post about it. In order:

  1. thanks! Wasn't aware of that
  2. i know, but having type annotations is a stylistic choice I like, it make the code more readable and a little faster
  3. I'll take a look asap
  4. could you please motivate? :)

Bye!

@mausch
Copy link

mausch commented Apr 1, 2012

Piping operators is unnecessary in this case, compare 2 |> (+) 2 |> (+) 4 with 2 + 2 + 4.

@adinapoli
Copy link
Author

Ok, in this case yes, I though you were generalizing to never use the pipe operator and I was puzzled :D
Anyway, I've completed my blog articled that should (hopefully) explain things a little better: http://alfredodinapoli.wordpress.com/2012/04/02/humbly-simple-f-maybe-monad-application-scenario/

Bye!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment