(While this question talks about SQL, because that’s what I’m using at the moment, it’s more of a generalised question really.)
I’ve got some code in which I am generating SQL queries which will be passed to a C# library which I have no control over.
That library takes the SQL string that I give it and replaces the parts of the text that start with “@” (without the quotes) with some actual values (in a parameters record) before execution.
I have stripped the main part of the code out to experiment with, as you can see below where I have three versions.
(The code below doesn’t call the library method with the values in ‘parameters’, it just outputs the SQL string.)
fsi.PrintWidth <- 200
type RenameParameters = { tableName : string ; rowId : int ; newName : string }
let idColumnName = "id"
let nameColumnName = "name"
module VersionOne =
// Basic and ample scope for spelling mistakes.
let rename =
let sql =
$"UPDATE @tableName
SET {nameColumnName} = @newName
WHERE {idColumnName} = @rowId"
fun (parameters : RenameParameters) ->
sql
module VersionTwo =
// Better, but string needs to be evaluated every time.
let rename (parameters : RenameParameters) =
let sql = $"UPDATE @{nameof parameters.tableName}
SET {nameColumnName} = @{nameof parameters.newName}
WHERE {idColumnName} = @{nameof parameters.rowId}"
sql
module VersionThree =
// A bit better, retains closure, but clumsy.
let rename =
let dummy = { RenameParameters.tableName = "dummy" ; rowId = 0 ; newName = "dummy" }
let sql = $"UPDATE @{nameof dummy.tableName}
SET {nameColumnName} = @{nameof dummy.newName}
WHERE {idColumnName} = @{nameof dummy.rowId}"
fun (parameters : RenameParameters) ->
sql
let parameters = { tableName = "table" ; rowId = 1 ; newName = "newName" }
let v1 = VersionOne.rename parameters
let v2 = VersionTwo.rename parameters
let v3 = VersionThree.rename parameters
I will need to do the same sort of thing in lots of functions, in multiple modules, but the records will always only be simple records with no members or anything fancy.
I’ve looked at Fsharp.Reflection, e.g:
let names = FSharpType.GetRecordFields(typeof<RenameParameters>) |> Array.map (fun prop -> prop.Name)
…but that doesn’t seem to be what I’m looking for.
Can I get the field names in a better way, specifically without the ‘dummy’ value and without having to re-evaluate the string each time?