2012-05-02 15 views
11

Perché si verifica questo comportamento?OCaml Printf.sprintf

# Printf.sprintf ("Foo %d %s") 2 "bar";; 
- : string = "Foo 2 bar" 

# Printf.sprintf ("Foo %d"^" %s") 2 "bar";; 
    Printf.sprintf ("Foo %d"^" %s") 2 "bar";; 
Error: This expression has type string but an expression was expected of type 
     ('a -> 'b -> 'c, unit, string) format = 
      ('a -> 'b -> 'c, unit, string, string, string, string) format6 

Mi aspetto che la concatenazione di stringhe venga valutata per prima, quindi tutto procederà normalmente. Questo ha a che fare con l'inganno del sistema di tipi utilizzato da Printf?

risposta

17

Sì, ha a che fare con il tipo di inganno del sistema. Se si desidera creare una stringa di formato è necessario utilizzare il (^^) Operatore:

# Printf.sprintf ("Foo %d" ^^ " %s") 2 "bar";; 
- : string = "Foo 2 bar" 

Non sto profondamente istruiti in questo inganno, ma credo che il compilatore è disposto a promuovere una costante stringa di in un formato printf se il contesto di digitazione lo richiede. Tuttavia, il risultato di ("Foo %d"^" %s") non è una costante di stringa, quindi non viene promosso. L'operatore (^^) crea un contesto di battitura in cui entrambi gli operandi possono essere promossi se sono costanti di stringa.

Si può vedere perché dovrebbe essere una costante di stringa: altrimenti i tipi associati (dei valori da stampare) non possono essere determinati.

+1

Una bella spiegazione, grazie. Un punto minore, trovo la tua formulazione "l'operatore (^^) crea un contesto di digitazione .." non chiaro; questo operatore non ha alcuna magia dentro, è semplicemente digitato '... format -> ... format -> ... format', e type inferenza fa il lavoro di verificare che i suoi parametri siano di questo tipo, stringhe costanti. – gasche

+1

gasche ha ragione. Ciò che deve essere compreso è che quando si assegna una stringa a 'Print. * Printf' non gli si attribuisce un valore di tipo' Pervasives.string' gli si attribuisce un valore di tipo 'Pervasives.format6' (che non è uguale a 'Pervasives.string'). –

+0

Giusto, avrei dovuto dire che (^^) ha dato il tipo di benzina. Non c'è assolutamente magia in quella parte. L'unica magia (a quanto ho capito) è nella promozione delle costanti di stringa al tipo 'format' appropriato. –

8

Il problema si verifica molto più ampiamente rispetto all'operatore ^. Fondamentalmente, il compilatore OCaml deve sapere che la stringa di formato è una stringa letterale e che la stringa letterale deve essere conosciuta al momento della compilazione. Oppure, OCaml non può trasmettere la stringa in fase di compilazione a questo tipo di BLAHBLAH format6. Il modulo Printf funziona correttamente solo con stringhe di formato che sono completamente note al momento della compilazione o con stringhe di formato già convertite nel tipo BLAHBLAH format.

In genere è possibile risolvere questo problema utilizzando l'operatore ^^ e lanciando esplicitamente tutte le stringhe letterali alla BLAHBLAH format tipo prima utilizzare tali stringhe in codice.

Ecco un altro esempio:

# Printf.sprintf (if true then "%d" else "%d ") 2;; 
    Error: This expression has type string but an expression was expected of type 
    ('a -> 'b, unit, string) format = 
     ('a -> 'b, unit, string, string, string, string) format6 
    (* define a type abbreviation for brevity *) 
    # type ('a,'b) fformat = ('a ->'b, unit, string) format;; 
    type ('a, 'b) fformat = ('a -> 'b, unit, string) format 
    # Printf.sprintf (if true then ("%d":('a,'b)fformat) else ("%d ":('a,'b)fformat)) 2;; 
    - : string = "2" 

Il sistema OCaml non può riconoscere che if ... then "a" else "b" può essere gettato a BLAHBLAH format. Se lanci ogni stringa letterale a BLAHBLAH format, allora tutto funziona. (Nota: non funziona se si tenta di lanciare l'intero if/then/else-BLAHBLAH format, poiché OCaml non può verificare che la stringa è un letterale.)

L'origine del problema è il requisito di sicurezza di tipo: OCaml richiede che ci è un argomento del tipo corretto per ogni %d e %s ecc. e garantisce questo al tempo di compilazione. Non è possibile garantire la sicurezza del tipo con Printf a meno che l'intera stringa di formato non sia nota in fase di compilazione. Pertanto, è impossibile utilizzare Printf con una stringa di formato calcolata tramite un complicato algoritmo, ad es. Selezionando %s e %d a caso.

Quando si utilizza if/then/else per calcolare la stringa di formato, quindi OCaml cose, oh gee, questo è un algoritmo complicato ed è inutile verificare la sicurezza del tipo in fase di compilazione. L'operatore ^^ conosce i tipi BLAHBLAH format e produce il risultato corretto quando concatena le stringhe di formato.Ma if/then/else non sa di BLAHBLAH format e non esiste un'alternativa incorporata a if/then/else (ma credo che potresti definire una cosa simile).