MasterOfFoo is an F# library that intend to facilitate writing functions that act like the native printf one does.

I presented it as an experiment during september’s Paris F# Meetup (See the summary in french) but I feel like it’s now ready to be tested by more people.

It is now available on nuget as a preview version of 0.1.2 and the source code is on GitHub.

The API is described in more details in the readme but here is a small taste of the possibilities with a proof-of-concept sqlCommandf implementation :

module SqlCommandBuilder =
    open System.Text
    open BlackFox.MasterOfFoo
    open System.Data.Common
    open System.Data.SqlClient

    type internal SqlEnv<'Result, 'cmd when 'cmd :> DbCommand>(command: 'cmd, k) =
        inherit PrintfEnv<unit, unit, 'Result>(())
        let queryString = StringBuilder()
        let mutable index = 0

        override __.Finalize(): 'Result =
            command.CommandText <- queryString.ToString()
            k(command)

        override __.Write(s : PrintableElement) =
            let asPrintf = s.FormatAsPrintF()
            match s.ElementType with
            | PrintableElementType.FromFormatSpecifier ->
                let parameter =
                    if typeof<DbParameter>.IsAssignableFrom(s.ValueType) then
                        s.Value :?> DbParameter
                    else
                        let paramName = sprintf "@p%i" index
                        index <- index + 1

                        let parameter = command.CreateParameter()
                        parameter.ParameterName <- paramName
                        parameter.Value <- s.Value
                        parameter

                ignore(queryString.Append parameter.ParameterName)
                command.Parameters.Add parameter |> ignore
            | _ ->
                ignore(queryString.Append asPrintf)

        override __.WriteT(()) = ()

    let sqlCommandf (format : Format<'T, unit, unit, SqlCommand>) =
        doPrintfFromEnv format (SqlEnv(new SqlCommand (), id))

    let sqlRunf (connection: SqlConnection) (format : Format<'T, unit, unit, unit>) =
        let execute (cmd: SqlCommand) = cmd.ExecuteNonQuery() |> ignore
        doPrintfFromEnv format (SqlEnv(connection.CreateCommand(), execute))

// Create the parametrized query "SELECT * FROM users WHERE name=@p0"
let cmd = sqlCommandf "SELECT * FROM users WHERE name=%s" "vbfox"

sqlRunf connection "DELETE FROM users WHERE name=%s" "vbfox"