2010-06-25 17 views
10

Sto riscontrando un problema nel correggere un avvertimento che il compilatore OCaml mi fornisce.Soppressione dell'avviso di abbinamento completo in OCaml

Fondamentalmente sto analizzando un'espressione che può essere composta da Bool, Int e Float.

Ho una tabella dei simboli che tiene traccia di tutti i simboli dichiarate con il loro tipo:

type ast_type = Bool | Int | Float 
and variables = (string, int*ast_type) Hashtbl.t; 

dove int è l'indice utilizzato in seguito nella matrice di tutte le variabili.

devo poi un tipo concreto che rappresenta il valore in una variabile:

type value = 
    | BOOL of bool 
    | INT of int 
    | FLOAT of float 
    | UNSET 
and var_values = value array 

sto cercando di definire il comportamento di un riferimento variabile all'interno di un'espressione booleana in modo da quello che faccio è

  • verifica che la variabile è dichiarata
  • verifica che la variabile è di tipo bool

per fare questo ho questo codice (s è il nome della variabile):

| GVar s -> 
      begin 
       try 
        let (i,t) = Hashtbl.find variables s in 
         if (t != Bool) then 
          raise (SemanticException (BoolExpected,s)) 
         else 
          (fun s -> let BOOL v = Array.get var_values i in v) 
       with 
        Not_found -> raise (SemanticException (VarUndefined,s)) 
      end 

Il problema è che i miei assegni assicurano che l'elemento preso da var_values sarà di tipo BOOL of bool ma naturalmente questo vincolo isn' Visto dal compilatore che mi avverte:

Avviso P: questo abbinamento di motivi non è esaustivo. Ecco un esempio di un valore che non è compensata: (FLOAT _ | _ INT | UNSET)

Come faccio a risolvere questo tipo di problemi? Grazie in anticipo

+0

Come parte, è utile dichiarare il tipo "var_values ​​= array valore" se si intende astrarre e lasciare solo il suo nome visibile dall'esterno del modulo. Oltre a questa possibilità, è un alias di tipo inutile che può rendere i messaggi di errore meno leggibili. –

risposta

8

Questo è un problema che si può risolvere usando OCaml's polymorphic variants.

Ecco un codice OCaml compilabile che deduco presenta il problema:

type ast_type = Bool | Int | Float 
and variables = (string, int*ast_type) Hashtbl.t 

type value = 
    | BOOL of bool 
    | INT of int 
    | FLOAT of float 
    | UNSET 

and var_values = value array 

type expr = GVar of string 

type exceptioninfo = BoolExpected | VarUndefined 

exception SemanticException of exceptioninfo * string 

let variables = Hashtbl.create 13 

let var_values = Array.create 13 (BOOL false) 

let f e = 
    match e with 
    | GVar s -> 
    begin 
     try 
     let (i,t) = Hashtbl.find variables s in 
      if (t != Bool) then 
      raise (SemanticException (BoolExpected,s)) 
      else 
      (fun s -> let BOOL v = Array.get var_values i in v) 
     with 
     Not_found -> raise (SemanticException (VarUndefined,s)) 
    end 

Genera l'avviso:

File "t.ml", line 30, characters 42-48: 
Warning P: this pattern-matching is not exhaustive. 
Here is an example of a value that is not matched: 
(FLOAT _|INT _|UNSET) 

Qui è lo stesso codice trasformato da usare varianti polimorfiche. Questo codice viene compilato senza avvisi. Si noti che le varianti polimorfiche hanno una potenza espressiva maggiore rispetto ai tipi standard (qui si consente di esprimere che var_values è una matrice solo di BOOL), ma possono portare a avvertenze enigmatiche.

type ast_type = Bool | Int | Float 
and variables = (string, int*ast_type) Hashtbl.t 

type value = 
    [ `BOOL of bool 
    | `INT of int 
    | `FLOAT of float 
    | `UNSET ] 

and var_values = value array 

type expr = GVar of string 

type exceptioninfo = BoolExpected | VarUndefined 

exception SemanticException of exceptioninfo * string 

let variables = Hashtbl.create 13 

let var_values = Array.create 13 (`BOOL false) 

let f e = 
    match e with 
    | GVar s -> 
    begin 
     try 
     let (i,t) = Hashtbl.find variables s in 
      if (t != Bool) then 
      raise (SemanticException (BoolExpected,s)) 
      else 
      (fun s -> let `BOOL v = Array.get var_values i in v) 
     with 
     Not_found -> raise (SemanticException (VarUndefined,s)) 
    end 

Ecco i tipi desunti dalla OCaml sul codice sopra:

type ast_type = Bool | Int | Float 
and variables = (string, int * ast_type) Hashtbl.t 
type value = [ `BOOL of bool | `FLOAT of float | `INT of int | `UNSET ] 
and var_values = value array 
type expr = GVar of string 
type exceptioninfo = BoolExpected | VarUndefined 
exception SemanticException of exceptioninfo * string 
val variables : (string, int * ast_type) Hashtbl.t 
val var_values : [ `BOOL of bool ] array 
val f : expr -> 'a -> bool 
4

Dai un'occhiata a this e cerca "disabilita avvisi". Dovresti venire a una bandiera -w.

Se si desidera risolvere il problema in modo "ocamlish", penso che si debba fare in modo che il pattern corrisponda in modo esaustivo, ovvero coprire tutti i casi che potrebbero verificarsi.

Ma se non si desidera corrispondere a tutti i valori possibili, si potrebbe considerare l'utilizzo di caratteri jolly (vedere here), che copre tutti i casi che non si desidera gestire in modo esplicito.

+0

Sì, questo può aiutare ma esiste un modo per correggere l'avviso in modo sicuro ocamlish senza disabilitarli? – Jack

+2

@Jack Il vero modo OCamlish consiste nel creare il tipo di dati in modo che non ci siano valori possibili non utilizzati. Ma questo non è sempre pratico o desiderabile. Il secondo sarebbe quello di aggiungere un modello jolly finale ('_') che solleva un'eccezione ('Invalid_argument' o un'alternativa specifica del modulo). –

0

Whoops! Ho letto male la tua domanda Lasciando la mia risposta qui sotto per i posteri.

risposta Aggiornato: c'è un motivo per cui si sta facendo il check-in del hashtbl, o perché non si può avere i tipi di dati concreti (valore tipo) nella hashtbl? Ciò semplificherebbe le cose. Così com'è, è possibile spostare il controllo per bool al Array.get e utilizzare una chiusura:

| GVar s -> 
     begin 
      try 
       let (i,_) = Hashtbl.find variables s in 
        match (Array.get var_values i) with BOOL(v) -> (fun s -> v) 
        | _ -> raise (SemanticException (BoolExpected,s)) 
      with 
       Not_found -> raise (SemanticException (VarUndefined,s)) 
     end 

In alternativa penso che avrebbe più senso per semplificare il codice. Spostare i valori nella Hashtbl invece di avere un tipo, un indice e una matrice di valori. O semplicemente memorizza l'indice in Hashtbl e controlla il tipo nella matrice.

RISPOSTA ERRATA SOTTO:

È possibile sostituire l'altro, se con un fiammifero. Oppure si può sostituire il let con un fiammifero:

sostituire if/else:

| GVar s -> 
     begin 
      try 
       let (i,t) = Hashtbl.find variables s in 
        match t with Bool -> (fun s -> let BOOL v = Array.get var_values i in v) 
        | _ -> raise (SemanticException (BoolExpected,s)) 
      with 
       Not_found -> raise (SemanticException (VarUndefined,s)) 
     end 

sostituire lasciare:

| GVar s -> 
     begin 
      try 
       match (Hashtbl.find variables s) with (i, Bool) -> (fun s -> let BOOL v = Array.get var_values i in v) 
        | _ -> raise (SemanticException (BoolExpected,s)) 
      with 
       Not_found -> raise (SemanticException (VarUndefined,s)) 
     end 
+0

Il problema non è la corrispondenza sul tipo di variabile 'Bool | _ 'ma sul' lascia BOOL v = var_values. (i) 'dato che' var_values' è un 'value array' e costringo l'elemento i-esimo di tipo' BOOL v' (dato che sono sicuro perché io controllato l'hashtable 'variables') .. – Jack

+0

Sì, l'ho appena capito e ho aggiornato la mia risposta. –

3

In questo caso particolare, varianti polimorfiche, come spiegato da Pascal, sono una buona risposta.

A volte, tuttavia, sei bloccato con un caso impossibile. Poi trovo naturale per scrivere

(fun s -> match Array.get var_values i with 
      | BOOL v -> v 
      | _ -> assert false) 

Questo è molto meglio che usare il flag -w p che potrebbe nascondere altri, modello non esaustivo indesiderato partite.