Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Pondering Axum + F#

It’s been a while since I’ve posted about Axum as I’ve been posting about other asynchronous and parallel programming models.  After two releases of the Axum standalone language and lots of good user feedback, it’s time to ponder what could be with Axum.  Overall, the goals of Axum to provide an agent-based concurrency oriented system is important as we consider the emerging hardware trends.  Many of these ideas would in fact benefit most languages, whether mainstream or not.  With all the success that it has had, there have also been some issues as well, which leads me to wonder, what else could we do here?  Let step through some of those issues today and see where we could go.

Should It Be Its Own Language?

One of the highlighted concerns was the need to have yet another language.  Creating and maintaining languages is a large effort, not to be undertaken lightly at any organization.  To base your language on C# while adding additional constructs was a great way to get started and create some buzz and buy-in, but it also brought its own set of problems. 

One problem is that it is an enormous effort to keep on par with the C# compiler and add these constructs on a continuing basis.  The Spec# project, also based upon C# realized that it wasn’t feasible to keep up with the rapid pace of the language and instead went as a language neutral approach.  Not only keeping up with the language, but to enforce many functional programming constructs that Axum embraces such as immutability and purity are harder to enforce given a language which is built upon object-oriented and imperative approaches, an approach which encourage the encapsulation and manipulation of state.  So, instead of creating a separate language, why not fold these constructs into an existing language instead?

The Case For F#

Given the impedance mismatch between the C# language and the overall goals of Axum for a safe, isolated, agent-based system that is great for concurrency, why not consider another language such as F#?  Now that F# is a first-class citizen within Visual Studio going forward, there can be a strong case made.  Given some of the features of F#, which we will discuss, could potentially be a happy marriage with Axum.  The F# language and its associated libraries have many of the constructs that are essential to an agent-based messaging system, including immutability (albeit shallow), encouraging side effect free programming, and library functions such as the mailbox processor messaging classes.

In Praise Of Immutability

In the world of concurrency we find that shared mutable state is evil.  Often we have to protect certain parts by using low level constructs such as locks, semaphores, mutexes.  Because of this, we find that most people tend to avoid concurrency programming as it tends to be too hard to get just right.  Instead, communication through message passing in languages such as Axum and Erlang, immutability is the standard.  This way, we can freely reference the values without any worry of whether they will change underneath us.  Providing first-class support for immutability goes a long way towards making safe concurrency programming easier for the masses.  With the F# language, we get that approach by default and with rich types such as records, discriminated unions, lists, tuples and more. 

Of particular note, Discriminated Unions are quite useful in messaging systems to define the types of messages your system accepts.  For example, we could model our auction messages quite easily using discriminated unions to define which interactions are legal, such as the following code:

type AuctionMessage =
  | Offer of int * MailboxProcessor<AuctionReply>
  | Inquire of MailboxProcessor<AuctionReply>
and  AuctionReply =
  | Status of int * System.DateTime
  | BestOffer
  | BeatenOffer of int
  | AuctionConcluded of 
      MailboxProcessor<AuctionReply> * MailboxProcessor<AuctionReply>
  | AuctionFailed
  | AuctionOver

In this case, we have succinctly defined what messages can be processed.  Adding to this, Axum, through the use of data-flow networks and channel protocols could be a nice compliment to determine how these messages are processed.   Where else could they match up nicely?

Built For Concurrency

To talk about a potential marriage between F# and Axum, we need to talk about the language and its libraries and how they support concurrency.  From first class events, to asynchronous workflows, to mailbox processors, F# has a wide array of asynchronous and parallel programming libraries available out of the box.  In particular, the asynchronous workflows and the mailbox processor could match well with the Axum programming model.  Interestingly, Luca Bolognese has built upon the mailbox processor for his LAgent framework to show some more interesting scenarios where agent based programming could work.  This includes such things as advanced error handling, hot swapping, implementing MapReduce and so on.

With little effort, we can model such things as the standard ping-pong example using F# mailboxes:

let (<--) (m:MailboxProcessor<_>) msg = m.Post(msg)
type PingMessage = Ping of MailboxProcessor<PongMessage> | Stop
and  PongMessage = Pong

let ping iters (pong : MailboxProcessor<PingMessage>) =
  new MailboxProcessor<PongMessage>(fun inbox -> 
    pong <-- Ping inbox
    let rec loop pingsLeft = async { 
      let! msg = inbox.Receive()
      match msg with
      | Pong -> 
          if pingsLeft % 1000 = 0 then
            printfn "Ping: pong"
          if pingsLeft > 0 then
            pong <-- Ping inbox
            return! loop(pingsLeft - 1)
          else
            printfn "Ping: stop"
            pong <-- Stop
            return () }
    loop (iters - 1))
            
let pong () =
  new MailboxProcessor<PingMessage>(fun inbox -> 
    let rec loop pongCount = async { 
      let! msg = inbox.Receive()
      match msg with
      | Ping sender -> 
          if pongCount % 1000 = 0 then
            printfn "Pong: ping %d" pongCount
          sender <-- Pong
          return! loop (pongCount + 1) 
      | Stop -> printfn "Pong: stop"; return () }
    loop 0)

let ponger = pong()
let pinger = ping 100000 ponger
pinger.Start()
ponger.Start()

And you’ll notice from all of this, there are no mutable values anywhere nor shared global state and all communication happens through message passing.  We have certain limitations here such as we cannot remote these calls, so all interaction happens inside the single application.  Not only that, but how do we enforce no shared state?

What Would It Look Like?

So, now that we looked at some of the reasons why F# and Axum might be a good marriage, what might it actually look like?  That’s a great question!  Some of the concepts from Axum could map perfectly to F# whereas some might need a little more coaxing.  Let’s go back to the basic building blocks of Axum.  They consist of the following:

  • Agents and domains
  • Channels
  • Schema
  • Networks

The first three items are concerned mostly with state isolation and the exchange of information between our isolated regions of our application, while the last item deals more with the how messages are passed. 

The domain is the most basic isolation concept where all data is encapsulated and only constructors are exposed outside of the domain definition.  Agents are defined to run within the isolated space of a domain with each agent on a separate thread of control.  These agents are exposed to one another through passing messages back and forth via channels which define ports through which our data flows.  The data that passes between our domains can be defined in a schema, similar in nature of XML Schemas, to define structure and rules of our data.  Now to think about message passing, F# already has asynchronous behavior already built in to the mailbox processor, so we could take advantage here and build on top of it and our networks could guide us as to what order messages are passed.

Let’s look at some possible code examples of what things might look like.  I’ll choose just a couple as they might work nicely out of the box.  Let’s first look at how we might define a port which accepts a given message. 

type ProductInput = Multiply of int * int | Done
type ProductOutput = Product of int | AckDone

This way, we could define what our payload type for both incoming and outgoing.  Another area would be to look how schemas might be done.  In our earlier example, we used simple data types such as integers, but what about more complex types?  Using F#, we could think of using records here to simulate the not only the data, but what is and is not required:

type PersonSchema = 
  { Name     : string
    Employer : string
    Age      : int option }

let matt =
  { Name = "Matthew Podwysocki"
    Employer = "Microsoft Corporation"
    Age = None }

By marking the fields we require as standard fields and those which are optional as option types works out nicely.  Using F#, we’re able to have concise syntax with a language that is already defined and quite flexible.

Conclusion

As you can see from this post, a potential marriage between Axum and F# would certainly be a great fit.  Instead of focusing on Axum as a separate language, focusing the effort on top of an existing language which matches many of the design goals can bear a lot of fruit.  I believe Axum is important for the .NET programming stack, giving us more options on how to deal with concurrency and changing hardware architectures.  What do you think?

This entry was posted in Axum, Concurrency, F#, Functional Programming. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • eventhelix

    Heres an implementation of axum channels and agents.
    type PortName = string
    type Channel = string * seq
    let port : string -> PortName = id
    let channel name x : Channel = (name, x)
    let extends = ()
    type AgentMsg = Receive of string * AsyncReplyChannel | Post of PortName * obj
    type AgentType< 'ConstructerArgs>(name: string, channels: seq, body: Agent -> ‘ConstructerArgs -> Async) =
    let create(args) =
    let ports =
    channels
    |> Seq.collect snd
    |> Seq.map(fun name ->
    let mb =
    MailboxProcessor.Start(fun _ -> async{return()})
    (name, mb))
    |> Map.ofSeq

    let rec mbox: Agent =
    MailboxProcessor.Start(fun inbox ->
    async {
    while true do
    let! msg = inbox.Receive()
    match msg with
    |Post(name, msg) -> ports.[name].Post(msg)
    |Receive(name, reply) ->
    let! msg = ports.[name].Receive()
    reply.Reply(msg)
    })
    body mbox args |> Async.Start
    mbox
    member self.Name = name
    member self.CreateInNewDomain(args) = create(args)
    and Agent = MailboxProcessor
    let (?< -) (m: Agent) x y = m.Post <| Post(x,y)
    let(?) (m: Agent) x = m.PostAndAsyncReply(fun r -> Receive(x, r))
    let agent name () chans body = AgentType(name, chans, body)
    // Samples
    let Foo =
    channel “Foo” [
    port “bar”;
    port “baz”;
    ]
    let FooBar =
    agent “FooBar” extends [Foo] (fun self () -> async {
    while true do
    let! msg = self?bar
    printfn “bar: %O” msg
    let! msg = self?baz
    printfn “baz: %O” msg
    }
    )
    let x = FooBar.CreateInNewDomain()
    x?bar < - 3
    x?baz <- "Hello"
    System.Console.ReadLine() |> ignore

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Konstantin,

    I’m going to have to disagree with you here. LINQ, although interesting, already had a lot of those features included and it wasn’t much of a stretch to include the others (anonymous types, type inference, extension methods, etc). Instead, languages would have to change their posture quite a bit to become immutable by default. The reason to focus on F# is that it supports many of the constructs already, but not only that, its syntax is quite lightweight and friendly to defining messages that is otherwise convoluted in languages such as C# and VB.NET.

    Matt

  • Konstantim Triger

    There are 2 different concepts, functionality and language binding to this functionality. I think that functionality should be available as a library usable from any .Net language. Then you may consider how some particular language is bound to this library, if needed adding special keywords to the language.

    This approach looks very native to the language agnostic nature of .net framework and proved itself very well with LINQ.

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Dimitri,

    Not quite as F# is a statically typed language, and one of the goals for Axum is safety through channels, ports, and domains. The interactions are a bit more rigid than Erlang which enforces discipline instead of the more free nature of Erlang and its dynamic typing.

    Matt

  • http://Mazmanov Dimitri

    Axum + F# – .NET Framework = Erlang

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Art,

    Thanks for the comments. I wouldn’t go as far as you would in terms of the language choice for Axum. To be fair, the large majority of .NET developers are C# developers and to target this language did indeed give them a wider audience for trying the language than F# would have. That said, there are things to be said for F# being the language of choice as it supports many of the features that are required by Axum out of the box. The others, I’m not as sure about without some changes being made to the language.

    Matt

  • http://semasiographic.com Art Scott

    Thanks Matthew.
    When I first heard of Axum, I thouhgt it was as you describe it should be. I am surprised it is not!

    Hopefully, reason will prevail over C#-illiness.
    Axum built on C# is a house built on sand, a bane.
    Built on a solid FP F# foundation Axum will be a boon.

    Keep up our gret work.
    Best Regards.
    Art