8
\$\begingroup\$

After reading this question, I've realized that I can do a lot to improve the quality of my question, so I've edited this question quite a bit.

I've been teaching myself F# in my spare time off and on for the last 6 months. I've finally started getting comfortable enough with the language to feel that a lot of my code could be much better. The problem is, I don't know what changes to make.

Here's what I'm interested in:

  • I'm using higher order functions to return functions for interacting with a specific message queue. Is this a good design. Would another F# developer feel comfortable with this?
  • Does this fit the idiomatic style of F#?
  • If you know RabbitMQ, are there any bugs which I may be creating here.

Here's the context of the little block of code:

I'm doing a lot of experiments with messaging systems and I've been using RabbitMQ as a messaging framework. There's a .Net library for RabbitMQ but it's written in and for C#. I can use it in F# but it feels clunky. I wanted a small wrapper around the RabbitMQ library which which convert it into a more functional interface. Also, this will hopefully make it very easy to use RabbitMQ in an F# program.

My wrapper handles the following for RabbitMQ:

  1. Connect to a RabbitMQ server
  2. Create a function which will let you read one message from a queue
  3. Create a function which will write a message to a queue
  4. For both 2 and 3, if the queue doesn't exist, the queue will be created (that's the declareQueue)

    module Client =
        let connectToRabbitMqServerAt address = 
            let factory = new ConnectionFactory(HostName = address)
            factory.CreateConnection()
    
        let openChannelOn (connection:IConnection) = connection.CreateModel()
    
        let private declareQueue (channel:IModel) queueName = 
            channel.QueueDeclare( queueName, false, false, false, null )
    
        let private publishToQueue (channel:IModel) queueName (message:string) =
            let body = Encoding.UTF8.GetBytes(message)
            channel.BasicPublish("", queueName, null, body)
    
        let createQueueReader channel queue = 
            declareQueue channel queue |> ignore
    
            fun () -> 
                let ea = channel.BasicGet(queue, true)
                if ea <> null then
                    let body = ea.Body
                    let message = Encoding.UTF8.GetString(body)
                    Some message
                else
                    None
    
        let createQueueWriter channel queue =
            declareQueue channel queue |> ignore
            publishToQueue channel queue
    

An example use case would be:

// open a connection to a RabbitMQ broker
let connection = connectToRabbitMqServerAt "localhost"
let myChannel = openChannelOn connection

// Connect to a queue for writing
let writeToHelloQueue = createQueueWriter myChannel "hello"

// write the message "Hello, World" to the queue "hello"
"Hello, World" |> writeToHelloQueue
\$\endgroup\$
3
\$\begingroup\$

Overall, this code looks very good. Just two nitpicks here:

  1. Give your function definitions lines to themselves:
let openChannelOn (connection:IConnection) = connection.CreateModel()
  1. Use match statements unless you are checking a boolean value directly:
if ea <> null then
    let body = ea.Body
    let message = Encoding.UTF8.GetString(body)
    Some message
else
    None

Becomes:

match ea with
| null -> None
| _ ->
    let body = ea.Body
    let message = Encoding.UTF8.GetString(body)
    Some message
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.