String Formatting in F#

open System

type Point = { X: int; Y: int }

let main() =
    // F# offers several printing options for formatting values.
    // For example, this prints an instance of our Point record.
    let p = { X = 1; Y = 2 }
    printfn "struct1: %A" p

    // The %A format specifier provides a default representation,
    // which includes field names for records.
    printfn "struct2: %A" p

    // To print the type of a value, use %O with GetType()
    printfn "type: %O" (p.GetType())

    // Formatting booleans is straightforward.
    printfn "bool: %b" true

    // There are many options for formatting integers.
    // Use %d for standard, base-10 formatting.
    printfn "int: %d" 123

    // This prints a binary representation.
    printfn "bin: %s" (Convert.ToString(14, 2))

    // This prints the character corresponding to the given integer.
    printfn "char: %c" (char 33)

    // %x provides hex encoding.
    printfn "hex: %x" 456

    // There are also several formatting options for floats.
    // For basic decimal formatting use %f.
    printfn "float1: %f" 78.9

    // %e and %E format the float in (slightly different versions of) scientific notation.
    printfn "float2: %e" 123400000.0
    printfn "float3: %E" 123400000.0

    // For basic string printing use %s.
    printfn "str1: %s" "\"string\""

    // To double-quote strings, use %O.
    printfn "str2: %O" "\"string\""

    // To print a representation of a pointer, use %O with the address-of operator.
    printfn "pointer: %O" (&&p)

    // When formatting numbers you will often want to control the width and precision
    // of the resulting figure. To specify the width of an integer, use a number after
    // the % in the format specifier. By default, the result will be right-justified
    // and padded with spaces.
    printfn "width1: |%6d|%6d|" 12 345

    // You can also specify the width of printed floats, though usually you'll also
    // want to restrict the decimal precision at the same time with the width.precision syntax.
    printfn "width2: |%6.2f|%6.2f|" 1.2 3.45

    // To left-justify, use the - flag.
    printfn "width3: |%-6.2f|%-6.2f|" 1.2 3.45

    // You may also want to control width when formatting strings,
    // especially to ensure that they align in table-like output.
    // For basic right-justified width:
    printfn "width4: |%6s|%6s|" "foo" "b"

    // To left-justify use the - flag as with numbers.
    printfn "width5: |%-6s|%-6s|" "foo" "b"

    // So far we've seen printfn, which prints the formatted string to stdout.
    // sprintf formats and returns a string without printing it anywhere.
    let s = sprintf "sprintf: a %s" "string"
    printfn "%s" s

    // You can format+print to TextWriters other than stdout using fprintf.
    fprintf stderr "io: an %s\n" "error"

main()

This F# code demonstrates various string formatting techniques, mirroring the functionality of the original Go example. Here are some key differences and notes:

  1. F# uses printfn for formatted printing, which is similar to Go’s fmt.Printf.

  2. The %A format specifier in F# is used for generic pretty-printing, which includes field names for records (similar to Go’s %+v).

  3. F# doesn’t have a direct equivalent to Go’s %#v for syntax representation, so we omit this part.

  4. To get the type of a value in F#, we use GetType() method with %O format specifier.

  5. For binary representation, we use Convert.ToString(value, 2) as F# doesn’t have a built-in binary format specifier.

  6. F# uses && operator to get the address of a value, which is similar to Go’s &.

  7. The width and precision formatting works similarly in F# as in Go.

  8. F# uses sprintf for formatting to a string, which is equivalent to Go’s fmt.Sprintf.

  9. For writing to stderr, F# uses fprintf stderr, which is similar to Go’s fmt.Fprintf(os.Stderr, ...).

This example showcases F#’s string formatting capabilities, which are quite similar to Go’s, allowing for easy transition between the two languages for this particular feature.