@ odpowiedź Lee jest prawidłowe na możliwe obejście, ale nie opisuje, co dzieje się z Twoim kodem.
W wyrażeniu printf "foo"
, "foo"
nie jest ciągiem do sformatowania. Zamiast tego jest to formatowanie samo przez się. Dokładniej, jest to literał łańcuchowy używany do wnioskowania o rzeczywistym typie TextWriterFormat<'T>
.
Podpis printf
jest:
printf : TextWriterFormat<'T> -> 'T
Od printfn ("DANNY")
nie zawiera żadnych specyfikatorów formatu, F # kompilator wywodzi się TextWriterFormat<unit>
, a cała ekspresja staje printfn ("DANNY")()
.
Ze zmienną niemożliwe jest statyczne przewidzenie, które specyfikatory formatu będą dostępne. Zastanów się, czy metoda ToLongTimeString()
mogła zwrócić ciągi znaków o numerze "%s"
lub "%d %d %d"
, jaki byłby prototyp zwracanej funkcji?
Przy wnioskowaniu prawidłowego typu, ciąg dosłowne działa dobrze, ale zmienna lub let
-binding nie działa:
let foo1 = "foo"
let bar = printf foo // does not compile
[<Literal>] let foo2 = "foo";; // see update below
let bar = printf foo2 // compiles fine
W każdym razie, wygląda o wiele bezpieczniejsze, aby zawsze używać formatu specyfikatory:
printf "%s" "DANNY"
printf "%s" (DateTime.Now.ToLongTimeString())
Aktualizacja: nie zapomnij wpisać podwójnego dwukropka ;;
po [<Literal>]
wartości w celu uniknięcia ostrzeżenie FS0058 w VS2013.
Ah; Zapomniałem, że kompilator robił tu magię; Myślałem o runtime. Ma doskonałe wyczucie, że działa tylko z ciągami dostępnymi podczas kompilacji! –
Czy niejawna konwersja z ciągu do TextWriterFormat coś F # robi specjalnie dla printf, czy możesz to zrobić również z własnymi typami? –
"Ma doskonałe wyczucie, że działa tylko z ciągami dostępnymi w czasie kompilacji!" -- Więc nie. 'let printsd fmt = Printf.TextWriterFormat int -> unit> fmt |> printfn' działa. A jeśli fmt nie ma odpowiednich specyfikatorów formatu, kończy się niepowodzeniem w czasie wykonywania. –