Подавить исчерпывающее соответствующее предупреждение в OCaml

У меня возникла проблема с исправлением предупреждения, которое мне предоставляет компилятор OCaml.

В основном я разбираю выражение, которое может быть составлено Bool, Int и Float.

У меня есть таблица символов, которая отслеживает все символы, объявленные с их типом:

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

где Int - это индекс, используемый позже в массиве всех переменных.

У меня есть конкретный тип, представляющий значение в переменной:

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

Я пытаюсь определить поведение ссылки на переменную внутри логического выражения, так что я делаю

  • проверьте, что переменная объявлена ​​
  • убедитесь, что переменная имеет тип bool

для этого у меня есть этот код (s - это имя переменной):

| 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

Проблема в том, что мои проверки гарантируют, что элемент, взятый из var_values, будет иметь тип BOOL of bool, но, конечно, это ограничение не видно компилятору, который предупреждает меня:

Предупреждение P: это сопоставление шаблонов не является исчерпывающим. Ниже приведен пример значения, которое не соответствует: (FLOAT _ | INT_ | UNSET)

Как я могу решить такие проблемы? Спасибо заранее

Ответ 1

Это проблема, которую вы можете решить с помощью полиморфных вариантов OCaml.

Вот какой компилятивный код OCaml, который я вывел, показывает вашу проблему:

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

Он генерирует предупреждение:

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)

Вот тот же код, преобразованный для использования полиморфных вариантов. Этот код компилируется без предупреждений. Обратите внимание, что полиморфные варианты имеют более выразительную силу, чем стандартные типы (здесь можно выразить, что var_values - это массив только BOOL), но они могут приводить к загадочным предупреждениям.

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

Ниже приведены типы, выводимые OCaml по приведенному выше коду:

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

Ответ 2

Посмотрите this и найдите "отключить предупреждения". Вы должны прийти к значку -w.

Если вы хотите исправить "ocamlish" путь, тогда я думаю, что вы должны сделать соответствие шаблона исчерпывающим, т.е. охватывать все случаи, которые могут произойти.

Но если вы не хотите сопоставлять все возможные значения, вы можете использовать подстановочный знак (см. здесь), который охватывает все которые вы не хотите явно обрабатывать.

Ответ 3

В этом конкретном случае полиморфные варианты, как объясняется Паскалем, являются хорошим ответом.

Иногда, однако, вы застряли в невозможном случае. Тогда я считаю естественным писать

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

Это намного лучше, чем использование флага -w p, который может скрыть другие, нежелательные не исчерпывающие соответствия шаблонов.

Ответ 4

Упс! Не обращай внимания на свой вопрос. Оставляя мой ответ ниже для потомков.

Обновленный ответ: есть ли причина, по которой вы делаете проверку в hashtbl, или почему у вас не могут быть конкретные типы данных (значение типа) в hashtbl? Это упростит ситуацию. Как бы то ни было, вы можете перенести проверку на bool на Array.get и использовать закрытие:

| 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

В качестве альтернативы, я думаю, что было бы разумнее упростить ваш код. Переместите значения в Hashtbl вместо того, чтобы иметь тип, индекс и массив значений. Или просто сохраните индекс в Hashtbl и проверьте тип в массиве.

НЕПРАВИЛЬНЫЙ ОТВЕТ НИЖЕ:

Вы можете заменить if else на совпадение. Или вы можете заменить let на совпадение:

заменить 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

заменить let:

| 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