-->

ocaml %identity function

2020-08-13 08:14发布

问题:

I'm wondering why do we need function like "%identity", which is the same as let a = a. Is it going to improve performance by using it ?

I'm introducing phantom typing in my program, calling identity functions a ton times to convert types, curious if "%identity" can reduce a bit of overhead.

回答1:

The %identity function is part of the implementation, not part of the OCaml language. It tells the compiler (in essence) that there's nothing to do to change the function's parameter to its return value. In other words, it tells the compiler to keep using the same value but to change its idea of the type. If used incorrectly, it basically voids all the excellent safety guarantees of the OCaml type system. Also, of course, it's not guaranteed to work in any other implementations of the language (including future releases of the INRIA compiler).

The inlining capability of the OCaml compiler should already guarantee that no code is generated for identity functions. So I would recommend that you just keep using them.

Update

To answer an unrelated question in the comments.... Assume you have function composition and the identity function:

let (<<) f g x = f (g x)
let id x = x

Then here are functions to add the elements of a list, to multiply the elements of a list, and to compose all the functions in a list:

# let sum l = List.fold_right (+) l 0;;
val sum : int list -> int = <fun>
# let product l = List.fold_right ( * ) l 1;;
val product : int list -> int = <fun>
# let composition l = List.fold_right (<<) l id;;
val composition : ('a -> 'a) list -> 'a -> 'a = <fun>

Examples:

# sum [2; 3; 5; 7];;
- : int = 17
# product [2; 4; 17];;
- : int = 136
# let mx = composition [(+) 1; ( * ) 10];;
val mx : int -> int = <fun>
# mx 2;;
- : int = 21

The point is that 0 is the identity for addition, 1 for multiplication, and id for function composition. id is useful all the time, just as 0 and 1 are.



回答2:

Using %identity as a foreign primitive can and will reduce the overhead associated with evaluating the (fun x -> x) closure every time it is applied.

The OCaml compilers special-case the % primitives: bytecomp/translcore.ml associates each one with a special built-in AST node (in the case of %identity it gets mapped to the Pidentity); the compilers match on the node and simplify the expression it is applied to. In the case of the native compiler, the relevant lines are:

  • asmcomp/closure.ml lines 197 and ss.: simplify %identity applied to a constant argument to the argument itself:

    begin match p with
      Pidentity -> make_const_int x
    | Pnegint -> make_const_int (-x)
    
  • asmcomp/cmmgen.ml line 1047 and ss.: simplify %identity as the LHS of an application to evaluate the argument directly:

    match p with
      (* Generic operations *)
        Pidentity ->
          transl arg
    

The byte-code compiler has similar simplification rules for the primitive.