3 people like it.

Finalizing Tesco purchase

The sample shows two different reprezentations of Tesco checkout. The first one stores scanned items - as a list of either purchase or cancel items - and the second stores final bill with product and total quantity. The snippet implements transformation that corresponds to finalizing the purchase.

Types representing Tesco products

1: 
2: 
3: 
4: 
5: 
6: 
type Code = string
type Name = string
type Price = decimal
type Picture = string
type Product = Product of Code * Name * Picture * Price
type Quantity = decimal

Representation using scanned items

1: 
2: 
3: 
4: 
5: 
6: 
type LineItem = 
  | SaleLineItem of int * Product * Quantity
  | CancelLineItem of int

/// Purchase is a list of items as they were scanned
type LinePurchase = list<LineItem>

Representation for the final bill

1: 
2: 
/// Immutable map stores products and the purchased quantity
type FinalPurchase = Map<Product, Quantity>

Finalizing the purchase

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
/// This function takes a list of scanned lines and
/// produces a final bill. It first removes all cancelled
/// items and then groups products to get total quantity.
let calculateFinal (line:LinePurchase) : FinalPurchase =
  line
  |> Seq.choose (fun item ->
      match item with
      | SaleLineItem(id, prod, q) -> 
          // Check if the item has been cancelled
          let cancelled =
            line |> Seq.exists (fun item ->
              match item with
              | CancelLineItem cancelId when cancelId = id -> true
              | _ -> false)
          if cancelled then None
          else Some (prod, q)
      | _ -> None )

  // Group products and calculate total quantity
  |> Seq.groupBy (fun (prod, q) -> prod)
  |> Seq.map (fun (prod, items) -> 
        prod, Seq.sumBy snd items)
  |> Map.ofSeq
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
type Name = string

Full name: Script.Name
type Price = decimal

Full name: Script.Price
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.decimal

--------------------
type decimal = System.Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>
type Picture = string

Full name: Script.Picture
Multiple items
union case Product.Product: Code * Name * Picture * Price -> Product

--------------------
type Product = | Product of Code * Name * Picture * Price

Full name: Script.Product
type Code = string

Full name: Script.Code
type Quantity = decimal

Full name: Script.Quantity
type LineItem =
  | SaleLineItem of int * Product * Quantity
  | CancelLineItem of int

Full name: Script.LineItem
union case LineItem.SaleLineItem: int * Product * Quantity -> LineItem
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
union case LineItem.CancelLineItem: int -> LineItem
type LinePurchase = LineItem list

Full name: Script.LinePurchase


 Purchase is a list of items as they were scanned
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
type FinalPurchase = Map<Product,Quantity>

Full name: Script.FinalPurchase


 Immutable map stores products and the purchased quantity
Multiple items
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  override Equals : obj -> bool
  member Remove : key:'Key -> Map<'Key,'Value>
  ...

Full name: Microsoft.FSharp.Collections.Map<_,_>

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val calculateFinal : line:LinePurchase -> FinalPurchase

Full name: Script.calculateFinal


 This function takes a list of scanned lines and
 produces a final bill. It first removes all cancelled
 items and then groups products to get total quantity.
val line : LinePurchase
module Seq

from Microsoft.FSharp.Collections
val choose : chooser:('T -> 'U option) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.choose
val item : LineItem
val id : int
val prod : Product
val q : Quantity
val cancelled : bool
val exists : predicate:('T -> bool) -> source:seq<'T> -> bool

Full name: Microsoft.FSharp.Collections.Seq.exists
val cancelId : int
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
val groupBy : projection:('T -> 'Key) -> source:seq<'T> -> seq<'Key * seq<'T>> (requires equality)

Full name: Microsoft.FSharp.Collections.Seq.groupBy
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map
val items : seq<Product * Quantity>
val sumBy : projection:('T -> 'U) -> source:seq<'T> -> 'U (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.Seq.sumBy
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val ofSeq : elements:seq<'Key * 'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.ofSeq

More information

Link:http://fssnip.net/bA
Posted:4 years ago
Author:Tomas Petricek
Tags: tesco , domain modelling , dsl , discriminated union , transformation