Reactive Messaging Patterns with F# and Akka.NET


Message Endpoints

For more details and full analysis of each pattern described in this section, please refer to Chapter 9 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

Messaging Gateway

The Messaging Gateway pattern encapsulates access to the messaging system from the application/domain.

 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: 
type AggregateRef(id, cache) =
    interface ICanTell with
        member this.Tell (message: obj, sender: IActorRef) = cache <! CacheMessage(id, message, sender)

let order (mailbox: Actor<_>) =
    let rec loop amount = actor {
        let! message = mailbox.Receive ()
        match message with
        | InitializeOrder amount ->
            printfn "Initializing Order with %M" amount
            return! loop amount
        | ProcessOrder ->
            printfn "Processing Order is %A" message
            return! loop amount
    }
    loop 0m

let aggregateCache aggregateFunc (mailbox: Actor<_>) =
    let rec loop aggregateIds = actor {
        let! CacheMessage(id, actualMessage, sender) = mailbox.Receive ()
        let child = mailbox.Context.Child id
        let aggregate = if child = (ActorRefs.Nobody :> IActorRef) then spawn mailbox.Context id <| aggregateFunc // reconstitute aggregate state here if pre-existing
                        else child
        aggregate.Tell (actualMessage, sender)
        return! loop aggregateIds
    }
    loop Set.empty

type DomainModel(name) =
    let mutable aggregateTypeRegistry = Map.empty
    let system = System.create "system" <| Configuration.load ()
    member this.AggregateOf (typeName, id) =
        let (AggregateType cacheActor) = aggregateTypeRegistry |> Map.find typeName
        cacheActor <! RegisterAggregateId(id)
        AggregateRef(id, cacheActor)
    member this.RegisterAggregateType (typeName, aggregateFunc) =
        let actorRef = spawn system typeName <| aggregateCache aggregateFunc
        aggregateTypeRegistry <- aggregateTypeRegistry |> Map.add typeName (AggregateType actorRef)
    member this.Shutdown () = system.Shutdown ()

let orderType = "Order"
let model = DomainModel("OrderProcessing")
model.RegisterAggregateType (orderType, order)
let orderRef = model.AggregateOf (orderType, "123")

orderRef <! InitializeOrder 249.95m
orderRef <! ProcessOrder
Complete Code

Sections

Messaging Mapper

This pattern maps domain types to messages.

 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: 
type QueryMonthlyOrdersFor = QueryMonthlyOrdersFor of customerId: string
type ReallyBigQueryResult = ReallyBigQueryResult of messageBody: string
        
let orderQueryService (serializer: IMessageSerializer) (mailbox: Actor<_>) =
    let monthlyOrdersFor customerId = [ for i in [1 .. 10] -> sprintf "Order data %i" i ]
    let rec loop () = actor {
        let! QueryMonthlyOrdersFor(customerId) as message = mailbox.Receive ()
        printfn "OrderQueryService: Received %A" message
        let queryResult = monthlyOrdersFor customerId
        let messageBody = serializer.Serialize(queryResult)
        mailbox.Sender () <! ReallyBigQueryResult messageBody
        return! loop ()
    }
    loop ()

let caller orderQueryService (mailbox: Actor<_>) =
    orderQueryService <! QueryMonthlyOrdersFor "123"
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        printfn "Caller: result received: %A" message
        return! loop ()
    }
    loop ()

let orderQueryServiceRef = spawn system "orderQueryService" <| orderQueryService serializer
let callerRef = spawn system "caller" <| caller orderQueryServiceRef
Complete Code

Sections

Transactional Client/Actor

This pattern adds transactional behavior between senders and receivers.

1: 
// TBD: Akka Persistence is not fully supported yet
Complete Code

Sections

Polling Consumer

This pattern enables actors to have control of when an specific message is consumed.

 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: 
let workItemsProvider (mailbox: Actor<_>) =
    let rec loop workItemsNamed = actor {
        let allocateWorkItems numberOfItems =
            let allocatedWorkItems = 
                [ 1 .. numberOfItems ]
                |> List.map (fun itemCount -> 
                    let nameIndex = workItemsNamed + itemCount
                    { Name = "WorkItem" + nameIndex.ToString () })
            allocatedWorkItems, workItemsNamed + numberOfItems

        let! AllocateWorkItems numberOfItems = mailbox.Receive ()
        let allocatedWorkItems, workItemsNamed = allocateWorkItems(numberOfItems)
        mailbox.Sender () <! WorkItemsAllocated allocatedWorkItems
        return! loop workItemsNamed
    }
    loop 0

let workConsumer workItemsProvider (mailbox: Actor<_>) =
    mailbox.Defer (fun () -> mailbox.Context.Stop(workItemsProvider))
    let rec loop totalItemsWorkedOn = actor {
        let performWorkOn workItem =
            let totalItemsWorkedOn = totalItemsWorkedOn + 1
            if (totalItemsWorkedOn >= 15) then  mailbox.Context.Stop mailbox.Self else ()
            totalItemsWorkedOn

        let! message = mailbox.Receive ()
        match message with
        | WorkItemsAllocated workitems ->
            printfn "WorkItemsAllocated..."
            workitems |> List.iter (fun workItem -> mailbox.Self <! WorkOnItem(workItem))
            mailbox.Self <! WorkNeeded
            return! loop totalItemsWorkedOn
        | WorkNeeded ->
            printfn "WoorkNeeded..."
            workItemsProvider <! AllocateWorkItems 5
            return! loop totalItemsWorkedOn
        | WorkOnItem workItem ->
            printfn "Performed work on: %s" workItem.Name
            let totalItemsWorkedOn = performWorkOn workItem
            return! loop totalItemsWorkedOn
    }
    loop 0

let workItemsProviderRef = spawn system "workItemsProvider" workItemsProvider
let workConsumerRef = spawn system "workConsumer" <| workConsumer workItemsProviderRef

workConsumerRef <! WorkNeeded
Complete Code

Sections

Event-Driven Consumer

This pattern is already implemented by Akka.NET as actors are event-driven by design.

1: 
// No code example

Sections

Competing Consumers

This pattern allows to distribute the messages between multiple consumers.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
let workConsumer (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! workItem = mailbox.Receive ()
        printfn "%s for: %s" mailbox.Self.Path.Name workItem.Name
        return! loop ()
    }
    loop ()

let workItemsProviderRef = spawnOpt system "workItemsProvider" workConsumer [ Router(Akka.Routing.SmallestMailboxPool(5)) ]

[ 1 .. 100 ]
|> List.iter (fun itemCount -> workItemsProviderRef <! { Name = "WorkItem" + itemCount.ToString () })
Complete Code

Sections

Message Dispatcher

The Message Dispatcher pattern distributes messages across multiple actors in high workload scenarios.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
let workConsumer (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! workItem = mailbox.Receive ()
        printfn "%s for: %s" mailbox.Self.Path.Name workItem.Name
        return! loop ()
    }
    loop ()

let workItemsProvider = spawnOpt system "workItemsProvider" workConsumer [ Router(Akka.Routing.RoundRobinPool(5)) ]

workItemsProvider <! { Name = "WorkItem1" }
workItemsProvider <! { Name = "WorkItem2" }
workItemsProvider <! { Name = "WorkItem3" }
workItemsProvider <! { Name = "WorkItem4" }
workItemsProvider <! { Name = "WorkItem5" }
Complete Code

Sections

Selective Consumer

This pattern enables the selection/filtering of messages received by the consumer.

 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: 
let selectiveConsumer (consumerOfA: IActorRef) (consumerOfB: IActorRef) (consumerOfC: IActorRef) (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        match box message with
        | :? MessageTypeA -> 
            consumerOfA.Forward message
            return! loop ()
        | :? MessageTypeB -> 
            consumerOfB.Forward message
            return! loop ()
        | :? MessageTypeC -> 
            consumerOfC.Forward message
            return! loop ()
        | _ -> return! loop ()
    }
    loop ()

let consumerOfMessageTypeA (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        printfn "ConsumerOfMessageTypeA: %A" message
        return! loop ()
    }
    loop ()

let consumerOfMessageTypeB (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        printfn "ConsumerOfMessageTypeB: %A" message
        return! loop ()
    }
    loop ()

let consumerOfMessageTypeC (mailbox: Actor<_>) =
    let rec loop () = actor {
        let! message = mailbox.Receive ()
        printfn "ConsumerOfMessageTypeC: %A" message
        return! loop ()
    }
    loop ()

let consumerOfARef = spawn system "consumerOfA" consumerOfMessageTypeA
let consumerOfBRef = spawn system "consumerOfB" consumerOfMessageTypeB
let consumerOfCRef = spawn system "consumerOfC" consumerOfMessageTypeC
let selectiveConsumerRef = spawn system "selectiveConsumer" <| selectiveConsumer consumerOfARef consumerOfBRef consumerOfCRef

selectiveConsumerRef <! MessageTypeA
selectiveConsumerRef <! MessageTypeB
selectiveConsumerRef <! MessageTypeC
Complete Code

Sections

Durable Subscriber

The Durable Subscriber pattern ensures that the consumer does not miss any messages.

1: 
// TBD: Akka Persistence is not fully supported yet
Complete Code

Sections

Idempotent Receiver

This pattern allows the consumer to safely receive the same message multiple times.

Deduplication

 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: 
type Transaction = Transaction of transactionId: TransactionId * amount: Money
type AccountBalance = AccountBalance of accountId: AccountId * amount: Money
type AccountMessage = 
    | Deposit of transactionId: TransactionId * amount: Money
    | Withdraw of transactionId: TransactionId * amount: Money
    | QueryBalance

let account accountId (mailbox: Actor<_>) =
    let rec loop transactions = actor {
        let calculateBalance () = 
            let amount = 
                transactions 
                |> Map.toList
                |> List.sumBy (fun (_,Transaction(_, Money amount)) -> amount)
            printfn "Balance: %M" amount
            AccountBalance(accountId, Money amount)

        let! message = mailbox.Receive ()
        match message with
        | Deposit(transactionId, amount) -> 
            let transaction = Transaction(transactionId, amount)
            printfn "Deposit: %A" transaction
            return! loop (transactions |> Map.add transactionId transaction)
        | Withdraw(transactionId, Money amount) -> 
            let transaction = Transaction(transactionId, Money -amount)
            printfn "Withdraw: %A" transaction
            return! loop (transactions |> Map.add transactionId transaction)
        | QueryBalance -> 
            mailbox.Sender () <! calculateBalance () // this msg is sent to the deadletter
            return! loop transactions
    }
    loop Map.empty

let accountRef = spawn system "account" <| account (AccountId "acc1")
let deposit1 = Deposit(TransactionId "tx1", Money(100m))

accountRef <! deposit1
accountRef <! QueryBalance
accountRef <! deposit1
accountRef <! Deposit(TransactionId "tx2", Money(20m))
accountRef <! QueryBalance
accountRef <! deposit1
accountRef <! Withdraw(TransactionId "tx3", Money(50m))
accountRef <! QueryBalance
accountRef <! deposit1
accountRef <! Deposit(TransactionId "tx4", Money(70m))
accountRef <! QueryBalance
accountRef <! deposit1
accountRef <! Withdraw(TransactionId "tx5", Money(100m))
accountRef <! QueryBalance
accountRef <! deposit1
accountRef <! Deposit(TransactionId "tx6", Money(10m))
accountRef <! QueryBalance
Complete Code (Deduplication)

Become

 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: 
type Document = { Text: string option } with
    member this.DetermineClassification () =
        this.Text
        |> Option.fold (fun _ text ->
            match text.ToLower () with
            | text when text.Contains "low" -> "Low"
            | text when text.Contains "medium" -> "Medium"
            | text when text.Contains "high" -> "High"
            | _  -> "Unknown")
            "Unknown"
    member this.IsNotAttached with get () = this.Text |> Option.fold (fun _ text -> String.IsNullOrEmpty text) false 
    member this.IsDefined with get () = this.Text.IsSome

let riskAssessment (mailbox: Actor<_>) =
    let rec documented (document: Document) = actor {
        let! message = mailbox.Receive ()
        match message with
        | AttachDocument _ ->
            // already received; ignore
            return! documented document
        | ClassifyRisk ->
            mailbox.Sender () <! RiskClassified(document.DetermineClassification ())
            return! documented document
    }
    let rec undocumented (document: Document) = actor {
        let! message = mailbox.Receive ()
        match message with
        | AttachDocument documentText ->
            let document = { Text = Some documentText }
            return! documented document
        | ClassifyRisk ->
            mailbox.Sender () <! RiskClassified("Unknown")
            return! undocumented document
    }
    undocumented { Text = None }

let riskAssessmentRef = spawn system "riskAssessment" riskAssessment

let futureAssessment1: RiskClassified = riskAssessmentRef <? ClassifyRisk |> Async.RunSynchronously
printfn "%A" futureAssessment1
riskAssessmentRef <! AttachDocument("This is a HIGH risk.")
let futureAssessment2: RiskClassified = riskAssessmentRef <? ClassifyRisk |> Async.RunSynchronously
printfn "%A" futureAssessment2
Complete Code (Become)

Sections

Service Activator

The Service Activator pattern provides a way to invoke an internal application service when a specific message arrives.

1: 
// No code example

Sections

Multiple items
type AggregateRef =
  interface obj
  new : id:obj * cache:obj -> AggregateRef
  override Tell : message:obj * sender:'a0 -> obj

Full name: messageendpoints.AggregateRef

--------------------
new : id:obj * cache:obj -> AggregateRef
val id : obj
val cache : obj
val this : AggregateRef
override AggregateRef.Tell : message:obj * sender:'a0 -> obj

Full name: messageendpoints.AggregateRef.Tell
val message : obj
type obj = System.Object

Full name: Microsoft.FSharp.Core.obj
val sender : 'a
val order : mailbox:'a -> 'b

Full name: messageendpoints.order
val mailbox : 'a
val loop : ('a -> 'b)
val amount : 'a
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val aggregateCache : aggregateFunc:'a -> mailbox:'b -> 'c

Full name: messageendpoints.aggregateCache
val aggregateFunc : 'a
val aggregateIds : 'a
val id : x:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.id
Multiple items
module Set

from Microsoft.FSharp.Collections

--------------------
type Set<'T (requires comparison)> =
  interface IComparable
  interface IEnumerable
  interface IEnumerable<'T>
  interface ICollection<'T>
  new : elements:seq<'T> -> Set<'T>
  member Add : value:'T -> Set<'T>
  member Contains : value:'T -> bool
  override Equals : obj -> bool
  member IsProperSubsetOf : otherSet:Set<'T> -> bool
  member IsProperSupersetOf : otherSet:Set<'T> -> bool
  ...

Full name: Microsoft.FSharp.Collections.Set<_>

--------------------
new : elements:seq<'T> -> Set<'T>
val empty<'T (requires comparison)> : Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Set.empty
Multiple items
type DomainModel =
  new : name:string -> DomainModel
  member AggregateOf : typeName:'a0 * id:'a1 -> 'a2
  member RegisterAggregateType : typeName:string * aggregateFunc:'a0 -> unit
  member Shutdown : unit -> 'a0

Full name: messageendpoints.DomainModel

--------------------
new : name:string -> DomainModel
val name : string
val mutable aggregateTypeRegistry : Map<string,obj>
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 empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.empty
val system : obj
namespace System
val this : DomainModel
member DomainModel.AggregateOf : typeName:'a0 * id:'a1 -> 'a2

Full name: messageendpoints.DomainModel.AggregateOf
val typeName : 'a
val id : 'a
val find : key:'Key -> table:Map<'Key,'T> -> 'T (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.find
member DomainModel.RegisterAggregateType : typeName:string * aggregateFunc:'a0 -> unit

Full name: messageendpoints.DomainModel.RegisterAggregateType
val typeName : string
val actorRef : obj
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.add
member DomainModel.Shutdown : unit -> 'a0

Full name: messageendpoints.DomainModel.Shutdown
val orderType : string

Full name: messageendpoints.orderType
val model : DomainModel

Full name: messageendpoints.model
member DomainModel.RegisterAggregateType : typeName:string * aggregateFunc:'a0 -> unit
val orderRef : obj

Full name: messageendpoints.orderRef
member DomainModel.AggregateOf : typeName:'a0 * id:'a1 -> 'a2
Multiple items
union case QueryMonthlyOrdersFor.QueryMonthlyOrdersFor: customerId: string -> QueryMonthlyOrdersFor

--------------------
type QueryMonthlyOrdersFor = | QueryMonthlyOrdersFor of customerId: string

Full name: messageendpoints.QueryMonthlyOrdersFor
Multiple items
val string : value:'T -> string

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

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

Full name: Microsoft.FSharp.Core.string
Multiple items
union case ReallyBigQueryResult.ReallyBigQueryResult: messageBody: string -> ReallyBigQueryResult

--------------------
type ReallyBigQueryResult = | ReallyBigQueryResult of messageBody: string

Full name: messageendpoints.ReallyBigQueryResult
val orderQueryService : serializer:'a -> mailbox:'b -> 'c

Full name: messageendpoints.orderQueryService
val serializer : 'a
val monthlyOrdersFor : ('a -> string list)
val customerId : 'a
val i : int
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val loop : (unit -> 'a)
val caller : orderQueryService:obj -> mailbox:'a -> 'b

Full name: messageendpoints.caller
val orderQueryService : obj
val orderQueryServiceRef : obj

Full name: messageendpoints.orderQueryServiceRef
val callerRef : obj

Full name: messageendpoints.callerRef
val workItemsProvider : mailbox:'a -> 'b

Full name: messageendpoints.workItemsProvider
val workItemsNamed : 'a
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 map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
val workConsumer : workItemsProvider:'a -> mailbox:'b -> 'c

Full name: messageendpoints.workConsumer
val workItemsProvider : 'a
val totalItemsWorkedOn : 'a
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
val workItemsProviderRef : obj

Full name: messageendpoints.workItemsProviderRef
val workConsumerRef : obj

Full name: messageendpoints.workConsumerRef
val workConsumer : mailbox:'a -> 'b

Full name: messageendpoints.workConsumer
val itemCount : int
System.Int32.ToString() : string
System.Int32.ToString(provider: System.IFormatProvider) : string
System.Int32.ToString(format: string) : string
System.Int32.ToString(format: string, provider: System.IFormatProvider) : string
val workItemsProvider : obj

Full name: messageendpoints.workItemsProvider
val selectiveConsumer : consumerOfA:'a -> consumerOfB:'b -> consumerOfC:'c -> mailbox:'d -> 'e

Full name: messageendpoints.selectiveConsumer
val consumerOfA : 'a
val consumerOfB : 'a
val consumerOfC : 'a
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
val consumerOfMessageTypeA : mailbox:'a -> 'b

Full name: messageendpoints.consumerOfMessageTypeA
val consumerOfMessageTypeB : mailbox:'a -> 'b

Full name: messageendpoints.consumerOfMessageTypeB
val consumerOfMessageTypeC : mailbox:'a -> 'b

Full name: messageendpoints.consumerOfMessageTypeC
val consumerOfARef : obj

Full name: messageendpoints.consumerOfARef
val consumerOfBRef : obj

Full name: messageendpoints.consumerOfBRef
val consumerOfCRef : obj

Full name: messageendpoints.consumerOfCRef
val selectiveConsumerRef : obj

Full name: messageendpoints.selectiveConsumerRef
Multiple items
union case Transaction.Transaction: transactionId: obj * amount: obj -> Transaction

--------------------
type Transaction = | Transaction of transactionId: obj * amount: obj

Full name: messageendpoints.Transaction
Multiple items
union case AccountBalance.AccountBalance: accountId: obj * amount: obj -> AccountBalance

--------------------
type AccountBalance = | AccountBalance of accountId: obj * amount: obj

Full name: messageendpoints.AccountBalance
type AccountMessage =
  | Deposit of transactionId: obj * amount: obj
  | Withdraw of transactionId: obj * amount: obj
  | QueryBalance

Full name: messageendpoints.AccountMessage
union case AccountMessage.Deposit: transactionId: obj * amount: obj -> AccountMessage
union case AccountMessage.Withdraw: transactionId: obj * amount: obj -> AccountMessage
union case AccountMessage.QueryBalance: AccountMessage
val account : accountId:'a -> mailbox:'b -> 'c

Full name: messageendpoints.account
val accountId : 'a
val transactions : 'a
val toList : table:Map<'Key,'T> -> ('Key * 'T) list (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.toList
val sumBy : projection:('T -> 'U) -> list:'T list -> 'U (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.List.sumBy
val accountRef : obj

Full name: messageendpoints.accountRef
val deposit1 : AccountMessage

Full name: messageendpoints.deposit1
type Document =
  {Text: string option;}
  member DetermineClassification : unit -> string
  member IsDefined : bool
  member IsNotAttached : bool

Full name: messageendpoints.Document
Document.Text: string option
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
val this : Document
member Document.DetermineClassification : unit -> string

Full name: messageendpoints.Document.DetermineClassification
module Option

from Microsoft.FSharp.Core
val fold : folder:('State -> 'T -> 'State) -> state:'State -> option:'T option -> 'State

Full name: Microsoft.FSharp.Core.Option.fold
val text : string
System.String.ToLower() : string
System.String.ToLower(culture: System.Globalization.CultureInfo) : string
System.String.Contains(value: string) : bool
member Document.IsNotAttached : bool

Full name: messageendpoints.Document.IsNotAttached
module String

from Microsoft.FSharp.Core
member Document.IsDefined : bool

Full name: messageendpoints.Document.IsDefined
property Option.IsSome: bool
val riskAssessment : mailbox:'a -> 'b

Full name: messageendpoints.riskAssessment
val documented : (Document -> 'a)
val document : Document
member Document.DetermineClassification : unit -> string
val undocumented : (Document -> 'a)
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val riskAssessmentRef : 'a (requires member ( <? ) and member ( <! ) and member ( <? ))

Full name: messageendpoints.riskAssessmentRef
val futureAssessment1 : 'a

Full name: messageendpoints.futureAssessment1
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:System.Threading.CancellationToken -> 'T
val futureAssessment2 : 'a

Full name: messageendpoints.futureAssessment2
F# Project
Fork me on GitHub