Enforce consistency of function arguments naming

This commit is contained in:
Louis Gesbert 2023-02-27 09:50:42 +01:00
parent d3ccfe9b91
commit 2c97d5de14
13 changed files with 191 additions and 172 deletions

View File

@ -144,27 +144,31 @@ module Rule = struct
| Some _, None -> 1
end
let empty_rule (pos : Pos.t) (have_parameter : typ list option) : rule =
let empty_rule
(pos : Pos.t)
(parameters : (Uid.MarkedString.info * typ) list option) : rule =
{
rule_just = Expr.box (ELit (LBool false), Untyped { pos });
rule_cons = Expr.box (ELit LEmptyError, Untyped { pos });
rule_parameter =
(match have_parameter with
| Some typs -> Some (List.map (fun typ -> Var.make "dummy", typ) typs)
| None -> None);
Option.map
(List.map (fun (lbl, typ) -> Var.make (Marked.unmark lbl), typ))
parameters;
rule_exception = BaseCase;
rule_id = RuleName.fresh ("empty", pos);
rule_label = Unlabeled;
}
let always_false_rule (pos : Pos.t) (have_parameter : typ list option) : rule =
let always_false_rule
(pos : Pos.t)
(parameters : (Uid.MarkedString.info * typ) list option) : rule =
{
rule_just = Expr.box (ELit (LBool true), Untyped { pos });
rule_cons = Expr.box (ELit (LBool false), Untyped { pos });
rule_parameter =
(match have_parameter with
| Some typs -> Some (List.map (fun typ -> Var.make "dummy", typ) typs)
| None -> None);
Option.map
(List.map (fun (lbl, typ) -> Var.make (Marked.unmark lbl), typ))
parameters;
rule_exception = BaseCase;
rule_id = RuleName.fresh ("always_false", pos);
rule_label = Unlabeled;
@ -181,6 +185,7 @@ type meta_assertion =
type scope_def = {
scope_def_rules : rule RuleName.Map.t;
scope_def_typ : typ;
scope_def_parameters : (Uid.MarkedString.info * typ) list option;
scope_def_is_condition : bool;
scope_def_io : io;
}

View File

@ -67,8 +67,10 @@ type rule = {
module Rule : Set.OrderedType with type t = rule
val empty_rule : Pos.t -> typ list option -> rule
val always_false_rule : Pos.t -> typ list option -> rule
val empty_rule : Pos.t -> (Uid.MarkedString.info * typ) list option -> rule
val always_false_rule :
Pos.t -> (Uid.MarkedString.info * typ) list option -> rule
type assertion = expr boxed
type variation_typ = Increasing | Decreasing
@ -102,6 +104,7 @@ type io = {
type scope_def = {
scope_def_rules : rule RuleName.Map.t;
scope_def_typ : typ;
scope_def_parameters : (Uid.MarkedString.info * Shared_ast.typ) list option;
scope_def_is_condition : bool;
scope_def_io : io;
}

View File

@ -1240,6 +1240,8 @@ let init_scope_defs
Ast.scope_def_rules = RuleName.Map.empty;
Ast.scope_def_typ = v_sig.var_sig_typ;
Ast.scope_def_is_condition = v_sig.var_sig_is_condition;
Ast.scope_def_parameters =
(match v_sig.var_sig_parameters with [] -> None | ps -> Some ps);
Ast.scope_def_io = attribute_to_io v_sig.var_sig_io;
}
scope_def_map
@ -1253,6 +1255,10 @@ let init_scope_defs
Ast.scope_def_rules = RuleName.Map.empty;
Ast.scope_def_typ = v_sig.var_sig_typ;
Ast.scope_def_is_condition = v_sig.var_sig_is_condition;
Ast.scope_def_parameters =
(match v_sig.var_sig_parameters with
| [] -> None
| ps -> Some ps);
Ast.scope_def_io =
(* The first state should have the input I/O of the original
variable, and the last state should have the output I/O
@ -1296,6 +1302,10 @@ let init_scope_defs
Ast.scope_def_rules = RuleName.Map.empty;
Ast.scope_def_typ = v_sig.var_sig_typ;
Ast.scope_def_is_condition = v_sig.var_sig_is_condition;
Ast.scope_def_parameters =
(match v_sig.var_sig_parameters with
| [] -> None
| ps -> Some ps);
Ast.scope_def_io = attribute_to_io v_sig.var_sig_io;
}
scope_def_map)

View File

@ -55,6 +55,7 @@ type enum_context = typ EnumConstructor.Map.t
type var_sig = {
var_sig_typ : typ;
var_sig_is_condition : bool;
var_sig_parameters : (Uid.MarkedString.info * Shared_ast.typ) list;
var_sig_io : Surface.Ast.scope_decl_context_io;
var_sig_states_idmap : StateName.t IdentName.Map.t;
var_sig_states_list : StateName.t list;
@ -360,6 +361,11 @@ let process_data_decl
state_uid :: states_list ))
decl.scope_decl_context_item_states (IdentName.Map.empty, [])
in
let var_sig_parameters =
List.map
(fun (lbl, typ) -> lbl, process_type ctxt typ)
decl.scope_decl_context_item_parameters
in
{
ctxt with
scopes = ScopeName.Map.add scope scope_ctxt ctxt.scopes;
@ -368,6 +374,7 @@ let process_data_decl
{
var_sig_typ = data_typ;
var_sig_is_condition = is_cond;
var_sig_parameters;
var_sig_io = decl.scope_decl_context_item_attribute;
var_sig_states_idmap = states_idmap;
var_sig_states_list = states_list;

View File

@ -55,6 +55,7 @@ type enum_context = typ EnumConstructor.Map.t
type var_sig = {
var_sig_typ : typ;
var_sig_is_condition : bool;
var_sig_parameters : (Uid.MarkedString.info * Shared_ast.typ) list;
var_sig_io : Surface.Ast.scope_decl_context_io;
var_sig_states_idmap : StateName.t IdentName.Map.t;
var_sig_states_list : StateName.t list;

View File

@ -18,6 +18,7 @@
open Catala_utils
open Shared_ast
module D = Desugared.Ast
(** {1 Expression translation}*)
@ -212,7 +213,7 @@ let rec rule_tree_to_expr
~(is_reentrant_var : bool)
(ctx : ctx)
(def_pos : Pos.t)
(is_func : Desugared.Ast.expr Var.t list option)
(params : Desugared.Ast.expr Var.t list option)
(tree : rule_tree) : untyped Ast.expr boxed =
let emark = Untyped { pos = def_pos } in
let exceptions, base_rules =
@ -224,7 +225,7 @@ let rec rule_tree_to_expr
let substitute_parameter
(e : Desugared.Ast.expr boxed)
(rule : Desugared.Ast.rule) : Desugared.Ast.expr boxed =
match is_func, rule.Desugared.Ast.rule_parameter with
match params, rule.Desugared.Ast.rule_parameter with
| Some new_params, Some old_params_with_types ->
let old_params, _ = List.split old_params_with_types in
let old_params = Array.of_list old_params in
@ -240,7 +241,7 @@ let rec rule_tree_to_expr
(* should not happen *)
in
let ctx =
match is_func with
match params with
| None -> ctx
| Some new_params ->
ListLabels.fold_left new_params ~init:ctx ~f:(fun ctx new_param ->
@ -296,7 +297,7 @@ let rec rule_tree_to_expr
in
let exceptions =
List.map
(rule_tree_to_expr ~toplevel:false ~is_reentrant_var ctx def_pos is_func)
(rule_tree_to_expr ~toplevel:false ~is_reentrant_var ctx def_pos params)
exceptions
in
let default =
@ -304,7 +305,7 @@ let rec rule_tree_to_expr
(Expr.elit (LBool true) emark)
default_containing_base_cases emark
in
match is_func, (List.hd base_rules).Desugared.Ast.rule_parameter with
match params, (List.hd base_rules).Desugared.Ast.rule_parameter with
| None, None -> default
| Some new_params, Some ls ->
let _, tys = List.split ls in
@ -334,135 +335,123 @@ let translate_def
(ctx : ctx)
(def_info : Desugared.Ast.ScopeDef.t)
(def : Desugared.Ast.rule RuleName.Map.t)
(params : (Uid.MarkedString.info * typ) list option)
(typ : typ)
(io : Desugared.Ast.io)
~(is_cond : bool)
~(is_subscope_var : bool) : untyped Ast.expr boxed =
(* Here, we have to transform this list of rules into a default tree. *)
let is_def_func =
match Marked.unmark typ with TArrow (_, _) -> true | _ -> false
let check_params pvars =
match params, pvars with
| None, None -> true
| None, Some _ | Some _, None -> false
| Some pdefs, Some pvars -> (
try
List.for_all2
(fun (lbl, ty1) (var, ty2) ->
String.equal (Marked.unmark lbl) (Bindlib.name_of var)
&& Type.equal ty1 ty2)
pdefs pvars
with Invalid_argument _ -> false)
in
let is_rule_func _ (r : Desugared.Ast.rule) : bool =
Option.is_some r.Desugared.Ast.rule_parameter
let wrong_params =
RuleName.Map.bindings
(RuleName.Map.filter
(fun _ r -> not (check_params r.D.rule_parameter))
def)
in
let all_rules_func = RuleName.Map.for_all is_rule_func def in
let all_rules_not_func =
RuleName.Map.for_all (fun n r -> not (is_rule_func n r)) def
in
let is_def_func_param_typ : typ list option =
if is_def_func && all_rules_func then
match Marked.unmark typ with
| TArrow (t_param, _) -> Some t_param
| _ ->
Errors.raise_spanned_error (Marked.get_mark typ)
"The definitions of %a are function but it doesn't have a function \
type"
Desugared.Ast.ScopeDef.format_t def_info
else if (not is_def_func) && all_rules_not_func then None
else
let spans =
List.map
(fun (_, r) ->
( Some "This definition is a function:",
Expr.pos r.Desugared.Ast.rule_cons ))
(RuleName.Map.bindings (RuleName.Map.filter is_rule_func def))
@ List.map
(fun (_, r) ->
( Some "This definition is not a function:",
Expr.pos r.Desugared.Ast.rule_cons ))
(RuleName.Map.bindings
(RuleName.Map.filter (fun n r -> not (is_rule_func n r)) def))
in
Errors.raise_multispanned_error spans
"some definitions of the same variable are functions while others \
aren't"
in
let top_list = def_map_to_tree def_info def in
let is_input =
match Marked.unmark io.Desugared.Ast.io_input with
| OnlyInput -> true
| _ -> false
in
let is_reentrant =
match Marked.unmark io.Desugared.Ast.io_input with
| Reentrant -> true
| _ -> false
in
let top_value : Desugared.Ast.rule option =
if is_cond && ((not is_subscope_var) || (is_subscope_var && is_input)) then
(* We add the bottom [false] value for conditions, only for the scope
where the condition is declared. Except when the variable is an input,
where we want the [false] to be added at each caller parent scope. *)
Some
(Desugared.Ast.always_false_rule
(Desugared.Ast.ScopeDef.get_position def_info)
is_def_func_param_typ)
else None
in
if
RuleName.Map.cardinal def = 0
&& is_subscope_var
(* Here we have a special case for the empty definitions. Indeed, we could
use the code for the regular case below that would create a convoluted
default always returning empty error, and this would be correct. But it
gets more complicated with functions. Indeed, if we create an empty
definition for a subscope argument whose type is a function, we get
something like [fun () -> (fun real_param -> < ... >)] that is passed as
an argument to the subscope. The sub-scope de-thunks but the de-thunking
does not return empty error, signalling there is not reentrant variable,
because functions are values! So the subscope does not see that there is
not reentrant variable and does not pick its internal definition instead.
See [test/test_scope/subscope_function_arg_not_defined.catala_en] for a
test case exercising that subtlety.
To avoid this complication we special case here and put an empty error
for all subscope variables that are not defined. It covers the subtlety
with functions described above but also conditions with the false default
value. *)
&& not (is_cond && is_input)
(* However, this special case suffers from an exception: when a condition is
defined as an OnlyInput to a subscope, since the [false] default value
will not be provided by the calee scope, it has to be placed in the
caller. *)
then
let m = Untyped { pos = Desugared.Ast.ScopeDef.get_position def_info } in
let empty_error = Expr.elit LEmptyError m in
match is_def_func_param_typ with
| Some tys ->
Expr.make_abs
(Array.init (List.length tys) (fun _ -> Var.make "_"))
empty_error tys (Expr.mark_pos m)
| _ -> empty_error
if wrong_params <> [] then
Errors.raise_multispanned_error
((Some "Declared here", Marked.get_mark typ)
:: List.map
(fun (r, _) -> None, Marked.get_mark (RuleName.get_info r))
wrong_params)
"The arguments of these definitions don't match the declaration."
else
rule_tree_to_expr ~toplevel:true ~is_reentrant_var:is_reentrant ctx
(Desugared.Ast.ScopeDef.get_position def_info)
(Option.map
(fun l ->
ListLabels.mapi l ~f:(fun i _ ->
Var.make ("param" ^ string_of_int i)))
is_def_func_param_typ)
(match top_list, top_value with
| [], None ->
(* In this case, there are no rules to define the expression and no
default value so we put an empty rule. *)
Leaf
[Desugared.Ast.empty_rule (Marked.get_mark typ) is_def_func_param_typ]
| [], Some top_value ->
(* In this case, there are no rules to define the expression but a
default value so we put it. *)
Leaf [top_value]
| _, Some top_value ->
(* When there are rules + a default value, we put the rules as
exceptions to the default value *)
Node (top_list, [top_value])
| [top_tree], None -> top_tree
| _, None ->
Node
( top_list,
[
Desugared.Ast.empty_rule (Marked.get_mark typ)
is_def_func_param_typ;
] ))
let top_list = def_map_to_tree def_info def in
let is_input =
match Marked.unmark io.Desugared.Ast.io_input with
| OnlyInput -> true
| _ -> false
in
let is_reentrant =
match Marked.unmark io.Desugared.Ast.io_input with
| Reentrant -> true
| _ -> false
in
let top_value : Desugared.Ast.rule option =
if is_cond && ((not is_subscope_var) || (is_subscope_var && is_input))
then
(* We add the bottom [false] value for conditions, only for the scope
where the condition is declared. Except when the variable is an
input, where we want the [false] to be added at each caller parent
scope. *)
Some
(Desugared.Ast.always_false_rule
(Desugared.Ast.ScopeDef.get_position def_info)
params)
else None
in
if
RuleName.Map.cardinal def = 0
&& is_subscope_var
(* Here we have a special case for the empty definitions. Indeed, we could
use the code for the regular case below that would create a convoluted
default always returning empty error, and this would be correct. But it
gets more complicated with functions. Indeed, if we create an empty
definition for a subscope argument whose type is a function, we get
something like [fun () -> (fun real_param -> < ... >)] that is passed
as an argument to the subscope. The sub-scope de-thunks but the
de-thunking does not return empty error, signalling there is not
reentrant variable, because functions are values! So the subscope does
not see that there is not reentrant variable and does not pick its
internal definition instead. See
[test/test_scope/subscope_function_arg_not_defined.catala_en] for a
test case exercising that subtlety.
To avoid this complication we special case here and put an empty error
for all subscope variables that are not defined. It covers the subtlety
with functions described above but also conditions with the false
default value. *)
&& not (is_cond && is_input)
(* However, this special case suffers from an exception: when a condition
is defined as an OnlyInput to a subscope, since the [false] default
value will not be provided by the calee scope, it has to be placed in
the caller. *)
then
let m = Untyped { pos = Desugared.Ast.ScopeDef.get_position def_info } in
let empty_error = Expr.elit LEmptyError m in
match params with
| Some ps ->
let labels, tys = List.split ps in
Expr.make_abs
(Array.of_list
(List.map (fun lbl -> Var.make (Marked.unmark lbl)) labels))
empty_error tys (Expr.mark_pos m)
| _ -> empty_error
else
rule_tree_to_expr ~toplevel:true ~is_reentrant_var:is_reentrant ctx
(Desugared.Ast.ScopeDef.get_position def_info)
(Option.map
(List.map (fun (lbl, _) -> Var.make (Marked.unmark lbl)))
params)
(match top_list, top_value with
| [], None ->
(* In this case, there are no rules to define the expression and no
default value so we put an empty rule. *)
Leaf [Desugared.Ast.empty_rule (Marked.get_mark typ) params]
| [], Some top_value ->
(* In this case, there are no rules to define the expression but a
default value so we put it. *)
Leaf [top_value]
| _, Some top_value ->
(* When there are rules + a default value, we put the rules as
exceptions to the default value *)
Node (top_list, [top_value])
| [top_tree], None -> top_tree
| _, None ->
Node
(top_list, [Desugared.Ast.empty_rule (Marked.get_mark typ) params]))
let translate_rule ctx (scope : Desugared.Ast.scope) = function
| Desugared.Dependency.Vertex.Var (var, state) -> (
@ -471,9 +460,10 @@ let translate_rule ctx (scope : Desugared.Ast.scope) = function
(Desugared.Ast.ScopeDef.Var (var, state))
scope.scope_defs
in
let var_def = scope_def.scope_def_rules in
let var_typ = scope_def.scope_def_typ in
let is_cond = scope_def.scope_def_is_condition in
let var_def = scope_def.D.scope_def_rules in
let var_params = scope_def.D.scope_def_parameters in
let var_typ = scope_def.D.scope_def_typ in
let is_cond = scope_def.D.scope_def_is_condition in
match Marked.unmark scope_def.Desugared.Ast.scope_def_io.io_input with
| OnlyInput when not (RuleName.Map.is_empty var_def) ->
(* If the variable is tagged as input, then it shall not be redefined. *)
@ -492,8 +482,8 @@ let translate_rule ctx (scope : Desugared.Ast.scope) = function
let expr_def =
translate_def ctx
(Desugared.Ast.ScopeDef.Var (var, state))
var_def var_typ scope_def.Desugared.Ast.scope_def_io ~is_cond
~is_subscope_var:false
var_def var_params var_typ scope_def.Desugared.Ast.scope_def_io
~is_cond ~is_subscope_var:false
in
let scope_var =
match ScopeVar.Map.find var ctx.scope_var_mapping, state with
@ -577,8 +567,8 @@ let translate_rule ctx (scope : Desugared.Ast.scope) = function
(* Now that all is good, we can proceed with translating this
redefinition to a proper Scopelang term. *)
let expr_def =
translate_def ctx def_key def def_typ
scope_def.Desugared.Ast.scope_def_io ~is_cond
translate_def ctx def_key def scope_def.D.scope_def_parameters
def_typ scope_def.Desugared.Ast.scope_def_io ~is_cond
~is_subscope_var:true
in
let subscop_real_name =

View File

@ -683,6 +683,7 @@ type scope_decl_context_scope = {
type scope_decl_context_data = {
scope_decl_context_item_name : lident Marked.pos;
scope_decl_context_item_typ : typ;
scope_decl_context_item_parameters : (lident Marked.pos * typ) list;
scope_decl_context_item_attribute : scope_decl_context_io;
scope_decl_context_item_states : lident Marked.pos list;
}

View File

@ -539,6 +539,8 @@ let scope_decl_item :=
ContextData {
scope_decl_context_item_name = i;
scope_decl_context_item_attribute = attr;
scope_decl_context_item_parameters =
List.map (fun (lbl, (base_t, m)) -> lbl, (Base base_t, m)) args_typ;
scope_decl_context_item_typ = type_from_args args_typ t;
scope_decl_context_item_states = states;
}
@ -561,6 +563,8 @@ let scope_decl_item :=
ContextData {
scope_decl_context_item_name = i;
scope_decl_context_item_attribute = attr;
scope_decl_context_item_parameters =
List.map (fun (lbl, (base_t, m)) -> lbl, (Base base_t, m)) args;
scope_decl_context_item_typ =
Ast.type_from_args args (Condition, pos_condition);
scope_decl_context_item_states = states;

View File

@ -14,15 +14,13 @@ scope S:
```catala-test-inline
$ catala typecheck
[ERROR] Inconsistent function definition for f1, the arguments differ
[ERROR] The arguments of these definitions don't match the declaration.
First definition here
┌─⯈ tests/test_func/bad/param_inconsistency.catala_en:8.2-9.41:
Declared here
┌─⯈ tests/test_func/bad/param_inconsistency.catala_en:4.22-29:
└─┐
8 │ definition f1 of x under condition cond
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
9 │ consequence equals decimal of (x * 2)
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
4 │ internal f1 content decimal depends on x content integer
│ ‾‾‾‾‾‾‾
┌─⯈ tests/test_func/bad/param_inconsistency.catala_en:10.2-11.28:

View File

@ -13,13 +13,13 @@ scope S:
```catala-test-inline
$ catala typecheck
[ERROR] Inconsistent function definition for f1, the arguments differ
[ERROR] The arguments of these definitions don't match the declaration.
First definition here
┌─⯈ tests/test_func/bad/param_inconsistency2.catala_en:8.2-46:
Declared here
┌─⯈ tests/test_func/bad/param_inconsistency2.catala_en:4.22-29:
└─┐
8 │ definition f1 of x equals decimal of (x * 2)
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
4 │ internal f1 content decimal depends on x content integer
‾‾‾‾‾‾‾
┌─⯈ tests/test_func/bad/param_inconsistency2.catala_en:9.2-10.28:

View File

@ -13,13 +13,13 @@ scope S:
```catala-test-inline
$ catala typecheck
[ERROR] Inconsistent function definition for f1, the arguments differ
[ERROR] The arguments of these definitions don't match the declaration.
First definition here
┌─⯈ tests/test_func/bad/param_inconsistency3.catala_en:8.2-46:
Declared here
┌─⯈ tests/test_func/bad/param_inconsistency3.catala_en:4.22-29:
└─┐
8 │ definition f1 of x equals decimal of (x * 2)
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
4 │ internal f1 content decimal depends on x content integer
‾‾‾‾‾‾‾
┌─⯈ tests/test_func/bad/param_inconsistency3.catala_en:9.2-10.28:

View File

@ -19,7 +19,7 @@ scope B:
$ catala Scopelang -s B
let scope B (b: bool|input) =
let a.f : integer → integer =
λ (param0: integer) → ⟨b && param0 >! 0 ⊢ param0 -! 1⟩;
λ (x: integer) → ⟨b && x >! 0 ⊢ x -! 1⟩;
call A[a]
```
@ -29,8 +29,8 @@ let A =
λ (A_in: A_in {"f_in": integer → integer}) →
let f : integer → integer = A_in."f_in" in
let f1 : integer → integer =
λ (param0: integer) → error_empty
⟨f param0 | true ⊢ ⟨true ⊢ param0 +! 1⟩⟩ in
λ (x: integer) → error_empty
⟨f x | true ⊢ ⟨true ⊢ x +! 1⟩⟩ in
A { }
```
@ -40,7 +40,7 @@ let B =
λ (B_in: B_in {"b_in": bool}) →
let b : bool = B_in."b_in" in
let a.f : integer → integer =
λ (param0: integer) → ⟨b && param0 >! 0 ⊢ param0 -! 1⟩ in
λ (x: integer) → ⟨b && x >! 0 ⊢ x -! 1⟩ in
let result : A {} = A (A_in { "f_in"= a.f }) in
B { }
```

View File

@ -43,7 +43,7 @@ $ catala Interpret -t -s HousingComputation
7 │ definition f of x equals (output of RentComputation).f of x
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
[LOG] ≔ RentComputation.direct.output: RentComputation { "f"= λ (param0: integer) → RentComputation { "f"= λ (param01: integer) → error_empty ⟨true ⊢ λ (param02: integer) → error_empty ⟨true ⊢ param02 +! 1⟩ param01 +! 1⟩ }."f" param0 }
[LOG] ≔ RentComputation.direct.output: RentComputation { "f"= λ (param0: integer) → RentComputation { "f"= λ (x: integer) → error_empty ⟨true ⊢ λ (x1: integer) → error_empty ⟨true ⊢ x1 +! 1⟩ x +! 1⟩ }."f" param0 }
[LOG] ← RentComputation.direct
[LOG] → RentComputation.f
[LOG] ≔ RentComputation.f.input0: 1
@ -70,20 +70,20 @@ $ catala Interpret -t -s HousingComputation
[LOG] ≔ HousingComputation.result: 3
[RESULT] Computation successful! Results:
[RESULT] f =
λ (param0: integer) → error_empty
λ (x: integer) → error_empty
⟨true ⊢
let result : RentComputation {"f": integer → integer} =
λ (RentComputation_in: RentComputation_in {}) →
let g : integer → integer = error_empty
(λ (param01: integer) → error_empty
⟨true ⊢ param01 +! 1⟩) in
(λ (x1: integer) → error_empty ⟨true ⊢ x1 +! 1⟩)
in
let f : integer → integer = error_empty
(λ (param01: integer) → error_empty
⟨true ⊢ g param01 +! 1⟩) in
(λ (x1: integer) → error_empty
⟨true ⊢ g x1 +! 1⟩) in
RentComputation { "f"= f } RentComputation_in { } in
let result1 : RentComputation {"f": integer → integer} =
RentComputation { "f"=
λ (param01: integer) → result."f" param01 } in
if true then result1 else ∅ ."f" param0
λ (param0: integer) → result."f" param0 } in
if true then result1 else ∅ ."f" x
[RESULT] result = 3
```