Community, Random

Scratching a 7-Year Itch

Today marks fszmq’s seventh birthday. Or maybe call it an anniversary? Very technically, the library’s a few weeks older than this. But seven years ago — on 2 May 2011 — I put the source code on GitHub.com, which officially introduced the project to the world. In observance of this milestone, I thought I’d talk a little about its origins, what I’ve learned over the years, and speculate a bit on what the future might hold for fszmq.


What, exactly, is fszmq?

fszmq is an MPLv2-licensed CLR (e.g. Mono, .NET) binding to the ZeroMQ distributed computing library. ZeroMQ, meanwhile, provides a wide range of building blocks for developing high-performance, message-passing systems.


Waaaaay Back When (in 2011)

To set the stage, let’s note that seven years ago I was in NYC, doing front-office development for a hedge fund. Ostensibly, I was building a medium-volume, gray-box trading system. But practically, I spent my time juggling loads of little tools needed for various business functions. And it had become painfully clear that we needed a better way of getting various services to coordinate. HTTP had too much overhead. WCF was too… well, WCF — with all the over-engineered pains one might expect. So we looked at various pieces of middle-ware. The big servers (MQSeries, TIBCO, et cetera) were well out of scope, due to time and budget constraints. The more approachable products (MSMQ, NServiceBus, et cetera) raised more than few performance and maintenance concerns. Then, one day, a colleague of mine mentioned a buddy of his (at another hedge fund) raved about a new tool they were using for high-frequency trading systems: ZeroMQ. In need of a solution, I took ZeroMQ for a test drive. I was very impressed by the speed, the flexibility and relative simplicity of the API, and the superb support. I was sold on a light-weight, decentralized messaging library. The only catch? I was not enjoying a return to C/C++ code. Fortunately, I had enough use-cases, with sufficient latency/throughout tolerance, that a managed wrapper was feasible. In fact, there was even a nascent version of a (now defunct) C# wrapper for libzmq (the native C library which serves at the canonical implementation of the ZMT protocol). However, the wrapper was buggy. And it had a fairly clumsy, poorly documented API. And was not receiving much support. So, rather than run back to C/C++, I decided to build my own wrapper. And because I was so taken by F# (at that point, I’d been using it for 4 years — 2 casually and then 2 professionally), I decided to put its P/Invoke support to the test. My initial results were solid. So, I put the work out on GitHub. Thus, fs-zmq (note the hyphen — now a long-forgotten vestige) came into existence, born out of equal parts need and curiosity.

Fast forward seven years. I think the library has held up fairly well. Certainly, it’s seen regular production use in a few different companies, both in and out of the financial sector. It’s also moved from my personal project to being part of the ZeroMQ organization. And it has received love and attention from nearly a dozen contributors over the years.

fszmq Milestones
Major events in the history of fszmq

The Good Parts of fszmq

But I want to take this opportunity to focus on the API. Growing fszmq has taught me a lot about the craft of library-as-interface. It has been especially instructive regarding the mixing of languages, both in terms of working with C from F# and in terms of providing a friendly experience to C# (or VB) consumers. As I’ve written about the P/Invoke “gotchas” elsewhere, I’d now like to talk about the aspects of the managed API which I think were rather successful.
It terms of a model, fszmq is primarily three types: Context, Socket, and Message. Each of these loosely follows the same pattern. A core type (Socket, et cetera) is primarily concerned with managing native memory safely and efficiently. I say “primarily” because the Context type is a little bit… well, special. In addition to managing its own native memory, a Context instance also tracks Socket instances so it may participate in program termination. This is unfortunately needed to get clean shutdown semantics (and is a long-standing pain point for wrappers of libzmq). Socket and Message instances, however, truly are only focused on allocating and releasing their own native memory. Meanwhile, the operations which define the meat of fszmq are all stateless functions which take an instance of a core type, e.g. Message (which is treated as an opaque token). This separation of data from behavior leads to a few interesting consequences. It aligns very closely to the underlying C API, reducing cognitive load. It also means adding individual behaviors is more isolated (and thus, testable). It also helps to increase the composability of certain workflows. If you think this sounds vaguely like the “abstract data type” pattern, you’re not wrong. The overall design is inspired by it. Although, it may be argued that fszmq deviates in some ways. At any rate, I’ve previously written a more detailed review (in case you’re interested).

There are two other aspects of the API which I feel are worth noting: cross-language support and documentation. There is an explicit effort throughout the code base to present a friendly and usable interface to non-F# consumers. I’ve written more generally about F# API design elsewhere. But, for fszmq this means several things:

  • Operational functions often serve “double duty” as extension methods, so as to appear like instance methods in C#.
  • In some places, additional overloads are given to convert between F#-centric types and more common BCL types.
  • CompiledNameAttribute is applied — judiciously — to help ensure discoverability when callers navigate the API.
  • An assembly-level ExtensionAttribute is emitted so that VB will properly detect extension methods, offering consumers in that language another possible means of expression.

Finally, I’ve always been impressed with the amount of genuinely useful documentation produced for libzmq. And I’m pleased that fszmq has tried to achieve something similar. Originally, this meant contributing to the ZGuide, an in-depth multi-language tour of ZeroMQ. However, in recent years, it has come to mean having comprehensive API documentation and instructional examples hosted along-side the actual fszmq repository.

Recent statistics on fszmq

The Not-So-Good Parts of fszmq

Of course, all libraries have warts, and fszmq is no exception. Seven years on, the API is showing its age in a few places. Additionally, there are a few things I’d do rather differently (if only I knew then what I know now). What follows is a partial airing of grievances, in rough order of how badly things irk me. Hopefully, not all of them have calcified to the point of permanence (but most of them have).

I should’ve (somehow) automated the code for getting and setting options on the core types — especially on Socket instances. It’s not particularly complicated code. But it’s tedious. And requires tweaking every time libzmq adds or deprecates an option (which is nearly every release). Ideally, I’d handle it with a type provider. But barring that (since they didn’t exist when I first started fszmq), I should’ve made code generation part of the build process (T4, GSL, et cetera).

Errors are a normal part of software development. But fszmq could handle them a lot better. There are several places where monadic error handling (i.e. railway-oriented programming) would enable much simpler and more composable workflows. There are also some places where errors really ought to be flat out swallowed. In these cases, it’s entire possible to anticipate and adapt. Finally, even in the places where raising an exception is the best course of action, fszmq could do better. Most times the underlying (native) error is just “passed through”. Encoding these errors into meaningful sub-classes of System.Exception (with any relevant meta-data) would provide a much better experience for consumers.

The polling API is too cumbersome. Polling is an essential technique in real-world applications. Yet the fszmq API — despite being reactive — feels very clunky. It requires complex mutable state management and closures in all but the simplest of scenarios. Now, admittedly, at least receiving scenarios can use tryPollInput (or TryGetInput, depending on the programming language). But even there you have a bit of overhead and no way to tune Message instance allocations.

Finally, how many custom operators is too many? In the case of fszmq — all of them! There’s no need for any of them. They just confuse things and add more surface area to maintain.


The Best Part of fszmq

Over the years I’ve come to appreciate a core strength of ZeroMQ is its tremendous community. And that certainly includes the following contributors who each had a hand in shaping fszmq. They forever have my respect and gratitude. Thanks!


Looking beyond 2017

So what’s next for fszmq? Well, there’s the usual tinkering (bug patching, keeping up with changes to libzmq, et cetera). But I’ve also begun producing content for an on-line video-based training course, which aims to get C# developers quickly building distributed solutions with ZeroMQ. There are also definite plans to further extend the documentation based on some community feedback. And I’ve begun work on adding a limited amount of integration between fszmq and F#’s async workflows. Also, some early-stage (mostly experimental) work was recently begun to make fszmq run on .NET Core. But really, I’m hoping to grow the contributors to fszmq. So why not check out the issue tracker and hack something up? Or at the very least, leave a suggestion for anything you think needs to be added or addressed in the library. I want to here from you!

Anyway, thanks for reading. And here’s to another seven years!


In Memoriam: Pieter Hintjens (3 December 1962 – 4 October 2016)

Nothing I’ve done with fszmq would’ve been possible without the vision and encouragement of Pieter. I’m honored to have collaborated (however briefly) with him. And no one I’ve met in my 18-year career has had a greater impact on my work — or my world view. I consider him a teacher. Hopefully, I have been and continue to be a worthy student.


 

Random, Source Code

Tips & Tricks to Improve Your F# Library’s Public API

This post is part of the 2016 F# Advent Calendar.

With the addition of F# in 2010, the .NET run-time gained a terrific, production-grade, functional-first programming language. This post is aimed at anyone who uses that terrific language to develop .NET libraries.

Because, you see, there’s a bit of a problem we need to discuss.

One of the popularized strengths of F# is its ability to both consume and be consumed by other .NET languages. And yet, if not done carefully, consuming F# code from, for instance, Visual Basic can be an absolute nightmare. Fortunately, over the years, I’ve settled upon a few different techniques that can mitigate, and in some cases even obliterate, any unpleasantness. Of course, this article assumes you, as a library author, actually want other developers to have a pleasant experience consuming your work (regardless of the .NET language they employ). If that’s not how you feel, no worries — but you can stop reading now. The rest of this post really won’t appeal to you.

What follows are 12 guidelines, listed in descending order of priority, which each address one, or more, potentially awkward points of integration between F# and other .NET languages. It’s important to note: this advice is intended for the public API of your libraries. By all means, do whatever awesome, clever things you’d like internally. This list is just to help give it a pretty face.

1. Do limit the use of advanced generic constraints

F# supports a wider range of possible generic type constraints than either C# or VB. Not matter how useful, or cool, a constraint might seem (sorry, SRTP fans), it’s meaningless if a consumer can’t possibly comply with it. To that end, public APIs should only leverage the following types of constraints:

Constraint Syntax
Subtype type-parameter :> type
Constructor type-parameter : ( new : unit -> 'a )
Value Type type-parameter : struct
Reference Type type-parameter : not struct

2. Do expose TPL rather than Async workflows

Asynchronous programming in F#, via Async workflows, is a simply unintelligible mess in other languages. Fortunately, it’s quite easy to integrate System.Threading.Tasks.Task into a workflow. Tasks received as input can be sequenced via Async.AwaitTask:

let runAll tasks = 
  let rec loop agg work = async {
    match work with 
    | [  ] -> return List.rev agg
    | h::t -> let! v = Async.AwaitTask h
              let  r = v * 2
              return! loop (r::agg) t } 
  tasks |> loop []

Meanwhile, if you need to return an async workflow to a non-F# caller, you can leverage Async.StartAsTask:

let getUserPrefs conn uid =  
  async { use db = new DbUsers() 
          let! prefs = db.ExecuteAsync(conn,uid) 
          return marshal prefs }
  |> Async.StartAsTask

3. Prefer BCL collection types

F# ships with a small number of persistent functional collections. These are the bread and butter of functional programming. But they’re cumbersome and confusing in other languages. So, for input parameters and return values, consider converting to or from common collection types. For example, when working with List or Set:

(* NOT RECOMMENDED *)
let transform (value :int list) =
  // do stuff with values...

(* DO THIS INSTEAD *)
let transform (values :int seq) =
  let values' = Seq.toList values
  // do stuff with values' ...

Similarly, when working with Map:

(* NOT RECOMMENDED *)
let transform (table :Map<string,int>) =
  // do stuff with table ...

(* DO THIS INSTEAD *)
let transform (table :IDictionary<string,int>) =
  let table' = 
    table 
    |> Seq.map (function KeyValue (k,v) -> (k,v)) 
    |> Map
  // do stuff with table' ...

Note, in the previous samples, type signatures are for demonstration purposes only. Also, note that similar conversions (e.g. Set.toSeq, et cetera) can, and should, be used for return values.

4. Do provide conversions from standard delegates to F# functions

Owning to a number of very good technical and historical reasons, F# uses a different first-class function mechanism than other languages. And, while the F# compiler makes it pretty easy to turn (fun x -> x * 2) into a Func, the inverse is not so easy. So, it becomes important to provide some means of supporting the standard BCL delegates Func and Action (which is what C# and VB use for their first-class functions). This can take several different formats. For instance, we can give the caller the ability to handle converting from a common delegate to an F# function. If we define a utility like:

[<Qualified>]
type Fun =
  static member Of(act :Action<'T>) = (fun t -> act.Invoke t)

Then a VB consumer might use:

Dim opt = BizLogic.ImportantCalc(42)
If Option.IsSome(opt) Then
  Option.Iterate(Fun.Of(PrintOption), opt)

However, often I will provide an extension method which handles the conversion internally, saving the consumer a bit of work. For example, a method like:

  [<Extension>]
  static member IfSome(option, act :Action<'T>) =
    option |> Option.iter (Fun.Of withSome)

Would turn the previous consumer example into something a bit simpler:

Dim opt = BizLogic.ImportantCalc(42)
opt.IfSome(PrintOption)

5. Do emulate matching with higher-order functions

While C# and VB do not support the rich pattern matching enjoyed in F#, we can still leverage higher-order functions to approximate an expression-oriented API. This technique is especially effective with discriminated unions, as seen here:

[<Extension>]
static member Match(option, withSome :Func<'T,'R>, withNone :Func<'R>) =
  match option with
  | Some value  -> (Fun.Of withSome) value
  | None        -> (Fun.Of withNone) ()

Given the above definition, consuming C# code might look like this:

return ValidationSvc.Validate(input).Match(
  withSome: v  => v.ToString(),
  withNone: () => "<NOT SET>"
);

6. Prefer overloaded methods to optional parameters

One of the recurring themes of this post is: F# does things differently. And optional parameters are no exception. In F# they are based on the Option type. However, in other languages they are handled via dedicated syntax. Due to this mismatch, F# “optional” arguments are, in fact, required in a call from C# or VB. In fairness, you can achieve the non-F# behavior in F# by careful use of attributes. However, I generally find it easier to explicitly define overloads, which vary in the number of parameters, and thus give callers the effect of having optional arguments.

ASIDE: a more in-depth look at this topic, by Mauricio Scheffer, may be found here.

7. Do remember to properly export extension methods

I can’t stress this one enough. In order to properly comply with published specifications — and to support Visual Basic.NET even recognizing an extension method — F# code should decorate each method being provided with [<Extension>]. Additionally, any class which contains any such decorated methods should also be decorated with [<Extension>]. Finally, somewhere — anywhere — in a library which provides extension methods, you need to add an assembly-level attribute of the form [<Extension>].

ASIDE: for a more detailed explanation of this mechanism please see this blog post, by Lincoln Atkinson.

8. Do use classes to provide extension methods

F# actually offers two different ways to define extension methods. The first approach is to decorate a module and one, or more, functions inside that module.

[<Extension>]
module Socket =
  [<Extension>]
  let Send (socket :Socket) (frame :byte[]) =
    // ...

  [<Extension>]
  let SendAll (socket :Socket) (frame :byte[][]) =
    // ...

But, as an alternative, you can decorate a class and one, or more, static methods defined on that class. This second approach, besides more closely mirroring the consumer’s mental model, offers a slight advantage: it greatly simplifies the process of providing method overloads. You simply list the separate methods, and implement them as normal.

[<Extension>]
type Socket =
  [<Extension>]
  static member Send(socket :Socket, frame :byte[]) =
    // ...

  [<Extension>]
  static member Send(socket :Socket, frames :byte[][]) =
    // ...

With the first approach, because each function needs a unique name within the module, you must leverage the CompiledNameAttribute to “fake out” method overloads (note: see the next tip for more details).

9. Do make judicious use of CompiledNameAttribute

The CompiledNameAttribute, like much of F#, is a double-edged sword. It can be used to great effect, especially when trying to support C# or VB. But it can also lead to ruin, increasing complexity and confusion for no real benefit. Use with caution. The concern, you see, is that by using this attribute, you cause a construct to have two different names. One will be used by/visible to F#. While the other will be used by/visible to other languages and reflective meta-programming. However, this is sometimes exactly what’s needed. For example, while it often makes sense to collect all of your “language interop” extension methods into a single, dedicated class. For very simple functions, requiring no additional manipulation, it may make sense to avoid the extra layer of indirection. For example, this code:

[<Extension; CompiledName "Send">]
let sendAll (socket :Socket) (msg :byte[][]) =
  // ...

Would be consumed from F# as:

msg |> Socket.sendAll pub

And equally from VB as:

pub.Send(msg)

Another time where CompiledNameAttribute can be helpful in sorting out naming conflicts is when types and modules need to have similar names:

type Result<'T,'Error>

[<Qualified; Module(Suffix)>]
module Result =
  // ...

[<Extension; CompiledName "Result")>]
type ResultExtensions =
  // ...

As this example demonstrates, we can partition the functionality around an abstract data type. We can put all the F#-facing components into a module. Then provide the C#-facing equivalents in a class for of static methods. Adding CompiledName to the mix ensures a clean, per-language experince.

In F#:

// invoking a function on the Result module
Result.tryCatch (fun () -> getUserFromDb conn) 

And in C#:

// invoking a static method on the ResultExtensions class
Result.TryCatch(() => { return getUserFromDb(conn); });

10. Prefer records or unions over tuples or primitives

Folks might shy away from exposing F#-centric types like records or discriminated unions to other languages. However, especially if heading the previous guidelines, there’s no reason not to share these powerful constructs with C# and VB. In particular, using types like Option (rather than “null checking” or using a Nullable) can greatly improve the overall robustness of an API. Consider this example:

let postRequest body :bool =
  // ...

It can only tell a consumer “Yay!” or “Nay!”. Meanwhile, this example gives calls a much richer experience:

let postRequest body :Result<bool,exn> =
  // ...

ASIDE: for more details about this approach to error handling, please see the excellent work presented here, by Scott Wlaschin.

11. Do expose a LInQ provider (if appropriate)

While not appropriate for all domains, exposing F# functionality via Language-Integrated Query (hereafter, LInQ) can be an excellent bit of “sugar” for C# and VB. Consider this example (leveraging techniques discussed earlier), which tries to combine multiple Result instances:

var vsn = major.Match(
    m => minor.Match(
        n => revision.Match(
            r => string.Format($"{m}.{n}.{r}"),
            e => e.Message),
        e => e.Message),
    e => e.Message);

WriteLine(vsn);

Now look at the improvements LInQ offers:

var vsn = from m in major
          from n in minor
          from r in revision
          select string.Format($"{m}.{n}.{r}");

vsn.IfOK(WriteLine); 

It’s true that providing LInQ support will mean defining 3, or more, extension methods against a type. But it’s usually worth the effort.

12. Do NOT rely on type aliases

Type aliases can dramatically improve the intensionality of your F# code. Unfortunately, they are fully erased at compile time (i.e. stored in special metadata only read by other F# libraries). So a C# or VB consumer won’t see your well-intended, self-documenting type alias… just the raw type underneath.

Random

FSharpy Goodness from the Land of Monkeys

This isn’t much of a post… except to say how pleased I am with the latest release of Mono (2.10.3). Specifically, F# Interactive (fsi, to friends) finally runs correctly on my MacBook Pro (Snow Leopard). Yay! It no longer throws an exception when you issue the “#quit” command (or just “#q”, for the cool kids). Also, it no longer spawns a separate (headless) application just to re-host the GUI message-pump. This was a long-standing pain for me, because it would cause the terminal to lose focus rather abruptly. Of course, I haven’t really tested thoroughly to ensure no other issues have appeared. But, as of right now, I’m pleased as punch. Well done, you monkey-loving geniuses!