stringf by Mark Seemann
stringf is an F# function that invokes any ToString(string) method.
F# comes with the built-in string function, which is essentially an adapter over Object.ToString. That often comes in handy, because it lets you compose functions without having to resort to lambda expressions all the time.
Instead of writing this (to produce the string "A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z"):
['A' .. 'Z'] |> List.map (fun x -> x.ToString()) |> String.concat ", "
You can write this:
['A' .. 'Z'] |> List.map string |> String.concat ", "
That's nice, but some .NET types provide an overloaded ToString function taking a format string as argument:
- DateTimeOffset.ToString : format:string -> string
- DateTime.ToString : format:string -> string
- TimeSpan.ToString : format:string -> string
- Decimal.ToString : format:string -> string
While these methods can be useful, it would be nice to be able to use them as functions, like the
string function. The problem, it seems, is that this ToString overload is part of no interface or base class.
Statically typed duck typing
That's not a problem for F#, because it enables us to do statically typed duck typing!
In this case, you can define a
let inline stringf format (x : ^a) = (^a : (member ToString : string -> string) (x, format))
You can use the function like this:
> DateTimeOffset.Now |> stringf "o";; val it : string = "2015-05-07T14:24:09.8422893+02:00" > DateTimeOffset.Now |> stringf "T";; val it : string = "14:24:18" > DateTime.Now |> stringf "t";; val it : string = "14:24" > TimeSpan.FromDays 42. |> stringf "c";; val it : string = "42.00:00:00" > 0.42m |> stringf "p0";; val it : string = "42 %"
Perhaps it would be better to define more domain-specific functions like
time, etc., but it's interesting that such a function as stringf is possible.