Reactive Messaging Patterns with F# and Akka.NET


Message Transformation

For more details and full analysis of each pattern described in this section, please refer to Chapter 8 of Reactive Messaging Patterns with the Actor Model by Vaughn Vernon.

Sections

  1. Introduction
  2. Messaging with Actors
  3. Messaging Channels
  4. Message Construction
  5. Message Routing
  6. Message Transformation
  7. Message Endpoints
  8. System Management and Infrastructure

Envelope Wrapper

This pattern wraps messages received from external sources.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
type IReplyToSupport =
    abstract member Reply: obj -> unit
    abstract member SetUpReplyToSupport: string -> unit
type IRegisterCustomer = 
    inherit IReplyToSupport
    abstract member Message: string with get
type RabbitMQReplyToSupport() =
    let mutable returnAddress = String.Empty
    interface IReplyToSupport with
        member this.Reply message = printfn "RabbitMQReplyToSupport: Replying %A to \"%s\"" message returnAddress
        member this.SetUpReplyToSupport replyReturnAddress = returnAddress <- replyReturnAddress
type RegisterCustomerRabbitMQReplyToMapEnvelope(mapMessage: Map<string, string>) as this =
    inherit RabbitMQReplyToSupport()
    let this = this :> IReplyToSupport
    do this.SetUpReplyToSupport(mapMessage |> Map.find "returnAddress")
    let message = mapMessage |> Map.find "message"
    interface IRegisterCustomer with
        member this.Message with get () = message

let customerRegistrar (mailbox: Actor<IRegisterCustomer>) =
    let rec loop () = actor {
        let! registerCustomer = mailbox.Receive ()
        printfn "CustomerRegistrar: Received \"%s\"" registerCustomer.Message
        registerCustomer.Reply "hi"
        return! loop ()
    }
    loop ()

let receivedMessageAsMap _ = [ ("returnAddress", "http://caller/"); ("message", "hello") ] |> Map.ofList
let wireMessage = ()
let mapMessage = receivedMessageAsMap wireMessage

let customerRegistrarRef = spawn system "customerRegistrar" customerRegistrar

let registerCustomer = RegisterCustomerRabbitMQReplyToMapEnvelope(mapMessage)
customerRegistrarRef <! registerCustomer
Complete Code

Sections

Content Enricher

This pattern adapts messages by adding or removing information so they are suitable for other systems.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
type PatientDetails = { LastName: string; SocialSecurityNumber: string; Carrier: string }
type DoctorVisitCompleted = 
    DoctorVisitCompleted of patientId: string * firstName: string * date: DateTimeOffset * carrier: string option * lastName: string option * socialSecurityNumber: string option with
    static member Create (patientId, firstName, date, patientDetails) = 
        DoctorVisitCompleted(patientId, firstName, date, Some patientDetails.Carrier, Some patientDetails.LastName, Some patientDetails.SocialSecurityNumber)
    static member Create (patientId, firstName, date) = DoctorVisitCompleted(patientId, firstName, date, None, None, None)
type VisitCompleted = VisitCompleted of dispatcher: IActorRef

let accountingEnricherDispatcher (accountingSystemDispatcher: IActorRef) (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! (DoctorVisitCompleted(patientId, firstName, date, _, _, _)) = mailbox.Receive ()
        printfn "AccountingEnricherDispatcher: querying and forwarding."
        let lastName = "Doe"
        let carrier = "Kaiser"
        let socialSecurityNumber = "111-22-3333"
        let patientDetails = { LastName = lastName; SocialSecurityNumber = socialSecurityNumber; Carrier = carrier }
        let enrichedDoctorVisitCompleted = DoctorVisitCompleted.Create (patientId, firstName, date, patientDetails)
        accountingSystemDispatcher.Forward enrichedDoctorVisitCompleted
        return! loop ()
    }
    loop ()

let accountingSystemDispatcher (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! doctorVisitCompleted = mailbox.Receive ()
        printfn "AccountingSystemDispatcher: sending to Accounting System..."
        return! loop ()
    }
    loop ()

let scheduledDoctorVisit patientId firstName (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! (VisitCompleted dispatcher) = mailbox.Receive ()
        printfn "ScheduledDoctorVisit: completing visit."
        let completedOn = DateTimeOffset.UtcNow
        dispatcher <! DoctorVisitCompleted.Create(patientId, firstName, completedOn)
        return! loop ()
    }
    loop ()

let accountingSystemDispatcherRef = spawn system "accountingSystem" accountingSystemDispatcher
let accountingEnricherDispatcherRef = spawn system "accountingDispatcher" <| accountingEnricherDispatcher accountingSystemDispatcherRef
let scheduledDoctorVisitRef = spawn system "scheduledVisit" <| scheduledDoctorVisit "123456789" "John"

scheduledDoctorVisitRef <! VisitCompleted(accountingEnricherDispatcherRef)
Complete Code

Sections

Content Filter

The Content Filter pattern reduces/simplifies messages by removing information not required by the target.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
type Message =
    | FilteredMessage of light: string * ``and``: string * fluffy: string * message: string
    | UnfilteredPayload of largePayload: string

let messageContentFilter (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        match message with
        | UnfilteredPayload payload -> 
            printfn "MessageContentFilter: received unfiltered message: %s" payload
            mailbox.Sender () <! FilteredMessage("this", "feels", "so", "right")
        | FilteredMessage _ -> printfn "MessageContentFilter: unexpected"
        return! loop ()
    }
    loop ()

let messageExchangeDispatcher (mailbox: Actor<_>) =
    let messageContentFilter = spawn mailbox.Context "messageContentFilter" messageContentFilter
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        match message with
        | UnfilteredPayload payload ->
            printfn "MessageExchangeDispatcher: received unfiltered message: %s" payload
            messageContentFilter <! message
        | FilteredMessage _ -> printfn "MessageExchangeDispatcher: dispatching: %A" message
        return! loop ()
    }
    loop ()

let messageExchangeDispatcherRef = spawn system "messageExchangeDispatcher" messageExchangeDispatcher

messageExchangeDispatcherRef <! UnfilteredPayload "A very large message with complex structure..."
Complete Code

Sections

Claim Check

The Claim Check pattern splits a message into smaller parts allowing access to them on demand.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
type Part = Part of name: string
type ClaimCheck = ClaimCheck of number: string with
    static member Create () = ClaimCheck((Guid.NewGuid ()).ToString ())
type CheckedItem = CheckedItem of claimCheck: ClaimCheck * businessId: string * parts: Map<string, Part>
type CheckedPart = CheckedPart of claimCheck: ClaimCheck * partName: string * part: obj
type ProcessStep = ProcessStep of id: string * claimCheck: ClaimCheck
type ProcessMessage =
    | CompositeMessage of id: string * part1: Part * part2: Part * part3: Part
    | StepCompleted of id: string * claimCheck: ClaimCheck * stepName: string
type ItemChecker() =
    let mutable checkedItems = Map.empty
    member this.CheckedItemFor (businessId, parts) = CheckedItem(ClaimCheck.Create(), businessId, parts)
    member this.CheckItem (CheckedItem(claimCheck, businessId, parts) as item) = checkedItems <- checkedItems |> Map.add claimCheck item
    member this.ClaimItem claimCheck = checkedItems |> Map.find claimCheck
    member this.ClaimPart (claimCheck, partName) = 
        let (CheckedItem(_, _, parts)) = checkedItems |> Map.find claimCheck
        CheckedPart(claimCheck, partName, parts |> Map.find partName)
    member this.RemoveItem claimCheck = checkedItems <- checkedItems |> Map.remove claimCheck

let ``process`` steps (itemChecker: ItemChecker) (mailbox: Actor<_>) =
    let rec loop stepIndex = actor {
        let! message = mailbox.Receive ()
        match message with
        | CompositeMessage(id, (Part(part1Name) as part1), (Part(part2Name) as part2), (Part(part3Name) as part3)) -> 
            let parts = [ (part1Name, part1); (part2Name, part2); (part3Name, part3) ] |> Map.ofList
            let (CheckedItem(claimCheck, _, _) as checkedItem) = itemChecker.CheckedItemFor (id, parts)
            itemChecker.CheckItem checkedItem
            steps |> List.item stepIndex <! ProcessStep(id, claimCheck)
            return! loop stepIndex
        | StepCompleted(id, claimCheck, stepName) -> 
            if stepIndex < steps.Length then steps |> List.item stepIndex <! ProcessStep(id, claimCheck)
            else itemChecker.RemoveItem claimCheck
            return! loop <| stepIndex + 1
    }
    loop 0

let step1 (itemChecker: ItemChecker) (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! (ProcessStep(id, claimCheck) as processStep) = mailbox.Receive ()
        let claimedPart = itemChecker.ClaimPart (claimCheck, "partA1")
        printfn "Step1: processing %A with %A" processStep claimedPart
        mailbox.Sender () <! StepCompleted(id, claimCheck, "step1")
        return! loop ()
    }
    loop ()

let step2 (itemChecker: ItemChecker) (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! (ProcessStep(id, claimCheck) as processStep) = mailbox.Receive ()
        let claimedPart = itemChecker.ClaimPart (claimCheck, "partB2")
        printfn "Step2: processing %A with %A" processStep claimedPart
        mailbox.Sender () <! StepCompleted(id, claimCheck, "step2")
        return! loop ()
    }
    loop ()

let step3 (itemChecker: ItemChecker) (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! (ProcessStep(id, claimCheck) as processStep) = mailbox.Receive ()
        let claimedPart = itemChecker.ClaimPart (claimCheck, "partC3")
        printfn "Step3: processing %A with %A" processStep claimedPart
        mailbox.Sender () <! StepCompleted(id, claimCheck, "step3")
        return! loop ()
    }
    loop ()

let itemChecker = ItemChecker()
let step1Ref = spawn system "step1" <| step1 itemChecker
let step2Ref = spawn system "step2" <| step2 itemChecker
let step3Ref = spawn system "step3" <| step3 itemChecker
let processRef = spawn system "process" <| ``process`` [step1Ref; step2Ref; step3Ref] itemChecker

processRef <! CompositeMessage("ABC", Part("partA1"), Part("partB2"), Part("partC3"))
Complete Code

Sections

Normalizer

The normalizer pattern transforms messages that have different unsupported formats so they have a commmon supported one. It uses a Message Router and multiple Multiple Translators.

1: 
// No code example

Sections

Canonical Message Model

This pattern defines a common set of messages shared by multiple applications.

1: 
// No code example

Sections

type IReplyToSupport =
  interface
    abstract member Reply : obj -> unit
    abstract member SetUpReplyToSupport : string -> unit
  end

Full name: messagetransformation.IReplyToSupport
abstract member IReplyToSupport.Reply : obj -> unit

Full name: messagetransformation.IReplyToSupport.Reply
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
abstract member IReplyToSupport.SetUpReplyToSupport : string -> unit

Full name: messagetransformation.IReplyToSupport.SetUpReplyToSupport
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 IRegisterCustomer =
  interface
    inherit IReplyToSupport
    abstract member Message : string
  end

Full name: messagetransformation.IRegisterCustomer
abstract member IRegisterCustomer.Message : string

Full name: messagetransformation.IRegisterCustomer.Message
Multiple items
type RabbitMQReplyToSupport =
  interface IReplyToSupport
  new : unit -> RabbitMQReplyToSupport

Full name: messagetransformation.RabbitMQReplyToSupport

--------------------
new : unit -> RabbitMQReplyToSupport
val mutable returnAddress : string
module String

from Microsoft.FSharp.Core
val this : RabbitMQReplyToSupport
override RabbitMQReplyToSupport.Reply : message:obj -> unit

Full name: messagetransformation.RabbitMQReplyToSupport.Reply
val message : obj
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
override RabbitMQReplyToSupport.SetUpReplyToSupport : replyReturnAddress:string -> unit

Full name: messagetransformation.RabbitMQReplyToSupport.SetUpReplyToSupport
val replyReturnAddress : string
Multiple items
type RegisterCustomerRabbitMQReplyToMapEnvelope =
  inherit RabbitMQReplyToSupport
  interface IRegisterCustomer
  new : mapMessage:Map<string,string> -> RegisterCustomerRabbitMQReplyToMapEnvelope

Full name: messagetransformation.RegisterCustomerRabbitMQReplyToMapEnvelope

--------------------
new : mapMessage:Map<string,string> -> RegisterCustomerRabbitMQReplyToMapEnvelope
val mapMessage : Map<string,string>
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 this : RegisterCustomerRabbitMQReplyToMapEnvelope
val this : IReplyToSupport
abstract member IReplyToSupport.SetUpReplyToSupport : string -> unit
val find : key:'Key -> table:Map<'Key,'T> -> 'T (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.find
val message : string
override RegisterCustomerRabbitMQReplyToMapEnvelope.Message : string

Full name: messagetransformation.RegisterCustomerRabbitMQReplyToMapEnvelope.Message
val customerRegistrar : mailbox:'a -> 'b

Full name: messagetransformation.customerRegistrar
val mailbox : 'a
val loop : (unit -> 'a)
val receivedMessageAsMap : 'a -> Map<string,string>

Full name: messagetransformation.receivedMessageAsMap
val ofList : elements:('Key * 'T) list -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.ofList
val wireMessage : unit

Full name: messagetransformation.wireMessage
val mapMessage : Map<string,string>

Full name: messagetransformation.mapMessage
val customerRegistrarRef : obj

Full name: messagetransformation.customerRegistrarRef
val registerCustomer : RegisterCustomerRabbitMQReplyToMapEnvelope

Full name: messagetransformation.registerCustomer
type PatientDetails =
  {LastName: string;
   SocialSecurityNumber: string;
   Carrier: string;}

Full name: messagetransformation.PatientDetails
PatientDetails.LastName: string
PatientDetails.SocialSecurityNumber: string
PatientDetails.Carrier: string
Multiple items
union case DoctorVisitCompleted.DoctorVisitCompleted: patientId: string * firstName: string * date: obj * carrier: string option * lastName: string option * socialSecurityNumber: string option -> DoctorVisitCompleted

--------------------
type DoctorVisitCompleted =
  | DoctorVisitCompleted of patientId: string * firstName: string * date: obj * carrier: string option * lastName: string option * socialSecurityNumber: string option
  static member Create : patientId:string * firstName:string * date:'a0 -> DoctorVisitCompleted
  static member Create : patientId:string * firstName:string * date:'a0 * patientDetails:PatientDetails -> DoctorVisitCompleted

Full name: messagetransformation.DoctorVisitCompleted
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
static member DoctorVisitCompleted.Create : patientId:string * firstName:string * date:'a0 * patientDetails:PatientDetails -> DoctorVisitCompleted

Full name: messagetransformation.DoctorVisitCompleted.Create
val patientId : string
val firstName : string
val date : 'a
val patientDetails : PatientDetails
union case Option.Some: Value: 'T -> Option<'T>
static member DoctorVisitCompleted.Create : patientId:string * firstName:string * date:'a0 -> DoctorVisitCompleted

Full name: messagetransformation.DoctorVisitCompleted.Create
union case Option.None: Option<'T>
Multiple items
union case VisitCompleted.VisitCompleted: dispatcher: obj -> VisitCompleted

--------------------
type VisitCompleted = | VisitCompleted of dispatcher: obj

Full name: messagetransformation.VisitCompleted
val accountingEnricherDispatcher : accountingSystemDispatcher:'a -> mailbox:'b -> 'c

Full name: messagetransformation.accountingEnricherDispatcher
val accountingSystemDispatcher : 'a
static member DoctorVisitCompleted.Create : patientId:string * firstName:string * date:'a0 -> DoctorVisitCompleted
static member DoctorVisitCompleted.Create : patientId:string * firstName:string * date:'a0 * patientDetails:PatientDetails -> DoctorVisitCompleted
val accountingSystemDispatcher : mailbox:'a -> 'b

Full name: messagetransformation.accountingSystemDispatcher
val scheduledDoctorVisit : patientId:'a -> firstName:'b -> mailbox:'c -> 'd

Full name: messagetransformation.scheduledDoctorVisit
val patientId : 'a
val firstName : 'a
val accountingSystemDispatcherRef : obj

Full name: messagetransformation.accountingSystemDispatcherRef
val accountingEnricherDispatcherRef : obj

Full name: messagetransformation.accountingEnricherDispatcherRef
val scheduledDoctorVisitRef : obj

Full name: messagetransformation.scheduledDoctorVisitRef
type Message =
  | FilteredMessage of light: string * and: string * fluffy: string * message: string
  | UnfilteredPayload of largePayload: string

Full name: messagetransformation.Message
union case Message.FilteredMessage: light: string * and: string * fluffy: string * message: string -> Message
union case Message.UnfilteredPayload: largePayload: string -> Message
val messageContentFilter : mailbox:'a -> 'b

Full name: messagetransformation.messageContentFilter
val messageExchangeDispatcher : mailbox:'a -> 'b

Full name: messagetransformation.messageExchangeDispatcher
val messageContentFilter : 'a
val messageExchangeDispatcherRef : 'a (requires member ( <! ))

Full name: messagetransformation.messageExchangeDispatcherRef
Multiple items
union case Part.Part: name: string -> Part

--------------------
type Part = | Part of name: string

Full name: messagetransformation.Part
Multiple items
union case ClaimCheck.ClaimCheck: number: string -> ClaimCheck

--------------------
type ClaimCheck =
  | ClaimCheck of number: string
  static member Create : unit -> ClaimCheck

Full name: messagetransformation.ClaimCheck
static member ClaimCheck.Create : unit -> ClaimCheck

Full name: messagetransformation.ClaimCheck.Create
Multiple items
union case CheckedItem.CheckedItem: claimCheck: ClaimCheck * businessId: string * parts: Map<string,Part> -> CheckedItem

--------------------
type CheckedItem = | CheckedItem of claimCheck: ClaimCheck * businessId: string * parts: Map<string,Part>

Full name: messagetransformation.CheckedItem
Multiple items
union case CheckedPart.CheckedPart: claimCheck: ClaimCheck * partName: string * part: obj -> CheckedPart

--------------------
type CheckedPart = | CheckedPart of claimCheck: ClaimCheck * partName: string * part: obj

Full name: messagetransformation.CheckedPart
Multiple items
union case ProcessStep.ProcessStep: id: string * claimCheck: ClaimCheck -> ProcessStep

--------------------
type ProcessStep = | ProcessStep of id: string * claimCheck: ClaimCheck

Full name: messagetransformation.ProcessStep
val id : x:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.id
type ProcessMessage =
  | CompositeMessage of id: string * part1: Part * part2: Part * part3: Part
  | StepCompleted of id: string * claimCheck: ClaimCheck * stepName: string

Full name: messagetransformation.ProcessMessage
union case ProcessMessage.CompositeMessage: id: string * part1: Part * part2: Part * part3: Part -> ProcessMessage
union case ProcessMessage.StepCompleted: id: string * claimCheck: ClaimCheck * stepName: string -> ProcessMessage
Multiple items
type ItemChecker =
  new : unit -> ItemChecker
  member CheckItem : CheckedItem -> unit
  member CheckedItemFor : businessId:string * parts:Map<string,Part> -> CheckedItem
  member ClaimItem : claimCheck:ClaimCheck -> CheckedItem
  member ClaimPart : claimCheck:ClaimCheck * partName:string -> CheckedPart
  member RemoveItem : claimCheck:ClaimCheck -> unit

Full name: messagetransformation.ItemChecker

--------------------
new : unit -> ItemChecker
val mutable checkedItems : Map<ClaimCheck,CheckedItem>
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.empty
val this : ItemChecker
member ItemChecker.CheckedItemFor : businessId:string * parts:Map<string,Part> -> CheckedItem

Full name: messagetransformation.ItemChecker.CheckedItemFor
val businessId : string
val parts : Map<string,Part>
static member ClaimCheck.Create : unit -> ClaimCheck
member ItemChecker.CheckItem : CheckedItem -> unit

Full name: messagetransformation.ItemChecker.CheckItem
val claimCheck : ClaimCheck
val item : CheckedItem
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.add
member ItemChecker.ClaimItem : claimCheck:ClaimCheck -> CheckedItem

Full name: messagetransformation.ItemChecker.ClaimItem
member ItemChecker.ClaimPart : claimCheck:ClaimCheck * partName:string -> CheckedPart

Full name: messagetransformation.ItemChecker.ClaimPart
val partName : string
member ItemChecker.RemoveItem : claimCheck:ClaimCheck -> unit

Full name: messagetransformation.ItemChecker.RemoveItem
val remove : key:'Key -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.remove
val steps : 'a
val itemChecker : ItemChecker
val loop : ('a -> 'b)
val stepIndex : 'a
member ItemChecker.CheckedItemFor : businessId:string * parts:Map<string,Part> -> CheckedItem
member ItemChecker.CheckItem : CheckedItem -> unit
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val item : index:int -> list:'T list -> 'T

Full name: Microsoft.FSharp.Collections.List.item
member ItemChecker.RemoveItem : claimCheck:ClaimCheck -> unit
val step1 : itemChecker:ItemChecker -> mailbox:'a -> 'b

Full name: messagetransformation.step1
member ItemChecker.ClaimPart : claimCheck:ClaimCheck * partName:string -> CheckedPart
val step2 : itemChecker:ItemChecker -> mailbox:'a -> 'b

Full name: messagetransformation.step2
val step3 : itemChecker:ItemChecker -> mailbox:'a -> 'b

Full name: messagetransformation.step3
val itemChecker : ItemChecker

Full name: messagetransformation.itemChecker
val step1Ref : 'a

Full name: messagetransformation.step1Ref
val step2Ref : 'a

Full name: messagetransformation.step2Ref
val step3Ref : 'a

Full name: messagetransformation.step3Ref
val processRef : 'a (requires member ( <! ))

Full name: messagetransformation.processRef
F# Project
Fork me on GitHub