Loading ...
Sorry, an error occurred while loading the content.
 

[Cross-Post] Lazy vs. wrapping a function in (fun () -> x)

Expand Messages
  • deech_99
    Recently I was writing some Ocaml code to do some Unix file system management and I was mistakenly using let statements without knowing that they would be
    Message 1 of 5 , Jul 2, 2008
      Recently I was writing some Ocaml code to do some Unix file system
      management and I was mistakenly using let statements without knowing
      that they would be evaluated eagerly. EG.

      let x dir:string =
      let cd = (* Unix system call that cd's into dir *)
      and mkdir = (* Unix system call that makes dir *)
      in
      mkdir;
      cd;
      ;;

      I wanted to make the directory first then cd into it, but because the
      let statements evaluate right away so it tried to cd into a
      nonexistent directory and failed. I can make it do what I want using
      the Lazy module:

      let x dir:string =
      let cd = lazy (* Unix system call that cd's into dir *)
      and mkdir = lazy (* Unix system call that makes dir *)
      in
      force mkdir;
      force cd
      ;;

      But I can also wrap cp and mkdir into anonymous functions and achieve
      the same effect:
      let x dir:string =
      let cd = (fun () -> (* Unix system call that cd's into dir *))
      and mkdir = (fun () -> (* Unix system call that makes dir *))
      in
      mkdir ();
      cd ();
      ;;

      Is there a difference between the approaches in terms of efficiency,
      style etc? I find both to be equally readable.

      -deech
    • Robert Fischer
      I wrap things into functions, since lazy things can only be forced once, but functions can be executed over and over again. ~~ Robert.
      Message 2 of 5 , Jul 2, 2008
        I wrap things into functions, since lazy things can only be forced once, but functions can be
        executed over and over again.

        ~~ Robert.

        deech_99 wrote:
        > Recently I was writing some Ocaml code to do some Unix file system
        > management and I was mistakenly using let statements without knowing
        > that they would be evaluated eagerly. EG.
        >
        > let x dir:string =
        > let cd = (* Unix system call that cd's into dir *)
        > and mkdir = (* Unix system call that makes dir *)
        > in
        > mkdir;
        > cd;
        > ;;
        >
        > I wanted to make the directory first then cd into it, but because the
        > let statements evaluate right away so it tried to cd into a
        > nonexistent directory and failed. I can make it do what I want using
        > the Lazy module:
        >
        > let x dir:string =
        > let cd = lazy (* Unix system call that cd's into dir *)
        > and mkdir = lazy (* Unix system call that makes dir *)
        > in
        > force mkdir;
        > force cd
        > ;;
        >
        > But I can also wrap cp and mkdir into anonymous functions and achieve
        > the same effect:
        > let x dir:string =
        > let cd = (fun () -> (* Unix system call that cd's into dir *))
        > and mkdir = (fun () -> (* Unix system call that makes dir *))
        > in
        > mkdir ();
        > cd ();
        > ;;
        >
        > Is there a difference between the approaches in terms of efficiency,
        > style etc? I find both to be equally readable.
        >
        > -deech
        >
        >
      • Karl Zilles
        ... You can also write this as: let cd () = (* Unix system call that cd s into dir *)
        Message 3 of 5 , Jul 2, 2008
          deech_99 wrote:
          > let cd = (fun () -> (* Unix system call that cd's into dir *))

          You can also write this as:

          let cd () = (* Unix system call that cd's into dir *)
        • Adrien
          You should take care about and because the different operands can be evaluated in any order. In your example, let cd = (* Unix system call that cd s into dir
          Message 4 of 5 , Jul 2, 2008
            You should take care about "and" because the different operands can be
            evaluated in any order. In your example,

            let cd = (* Unix system call that cd's into dir *)
            and mkdir = (* Unix system call that makes dir *)

            you are not guaranteed that cd will be evaluated before mkdir. In fact
            it is guaranteed that there is no order. Therefore you could get the
            same results with

            let mkdir = (* Unix system call that makes dir *)
            and cd = (* Unix system call that cd's into dir *)


            The best way to avoid this problem is to use "let ... in" or ';' :

            let cd = (* Unix system call that cd's into dir *) in
            let mkdir = (* Unix system call that makes dir *) in ()


            As far as I'm concerned, I would write this at the top of the code :

            let cd = Unix.chdir (* which is equivalent to your "fun () -> ..." *)
            let mkdir = function s -> Unix.mkdir 0o777 s (* I know it's bad but
            that's just and example :p *)

            and use these shiny new cd and mkdir.


            By the way, why are you forcing the type of your dir variable ? (with
            dir:string )
            You really don't need to. ;)


            ---

            Adrien Nader


            2008/7/2 Robert Fischer <robert.fischer@...>:
            > I wrap things into functions, since lazy things can only be forced once, but functions can be
            > executed over and over again.
            >
            > ~~ Robert.
            >
            > deech_99 wrote:
            >> Recently I was writing some Ocaml code to do some Unix file system
            >> management and I was mistakenly using let statements without knowing
            >> that they would be evaluated eagerly. EG.
            >>
            >> let x dir:string =
            >> let cd = (* Unix system call that cd's into dir *)
            >> and mkdir = (* Unix system call that makes dir *)
            >> in
            >> mkdir;
            >> cd;
            >> ;;
            >>
            >> I wanted to make the directory first then cd into it, but because the
            >> let statements evaluate right away so it tried to cd into a
            >> nonexistent directory and failed. I can make it do what I want using
            >> the Lazy module:
            >>
            >> let x dir:string =
            >> let cd = lazy (* Unix system call that cd's into dir *)
            >> and mkdir = lazy (* Unix system call that makes dir *)
            >> in
            >> force mkdir;
            >> force cd
            >> ;;
            >>
            >> But I can also wrap cp and mkdir into anonymous functions and achieve
            >> the same effect:
            >> let x dir:string =
            >> let cd = (fun () -> (* Unix system call that cd's into dir *))
            >> and mkdir = (fun () -> (* Unix system call that makes dir *))
            >> in
            >> mkdir ();
            >> cd ();
            >> ;;
            >>
            >> Is there a difference between the approaches in terms of efficiency,
            >> style etc? I find both to be equally readable.
            >>
            >> -deech
            >>
            >>
            >
            > ------------------------------------
            >
            > Archives up to December 31, 2007 are also downloadable at http://www.connettivo.net/cntprojects/ocaml_beginners/
            > The archives of the very official ocaml list (the seniors' one) can be found at http://caml.inria.fr
            > Attachments are banned and you're asked to be polite, avoid flames etc.Yahoo! Groups Links
            >
            >
            >
            >
          • Jon Harrop
            ... This code is certainly broken and poor style anyway. Firstly, you have assigned the unit (void) return values of your expressions to two variables and then
            Message 5 of 5 , Jul 2, 2008
              On Wednesday 02 July 2008 22:08:25 deech_99 wrote:
              > Recently I was writing some Ocaml code to do some Unix file system
              > management and I was mistakenly using let statements without knowing
              > that they would be evaluated eagerly. EG.
              >
              > let x dir:string =
              > let cd = (* Unix system call that cd's into dir *)
              > and mkdir = (* Unix system call that makes dir *)
              > in
              > mkdir;
              > cd;
              > ;;

              This code is certainly broken and poor style anyway. Firstly, you have
              assigned the unit (void) return values of your expressions to two variables
              and then explicitly ignored them. Secondly, by using "and" instead of two
              separate let bindings you have left the order of evaluation undefined so the
              cd and mkdir could occur in either order. Finally, you have annotated a type
              as if it were the type of "dir" when, in fact, it is the return type of the
              function "x".

              I would have used:

              let x (dir: string) =
              (* mkdir *)
              ...Unix system call that makes dir...

              (* cd *)
              ...Unix system call that cd's into dir...

              > I wanted to make the directory first then cd into it, but because the
              > let statements evaluate right away so it tried to cd into a
              > nonexistent directory and failed. I can make it do what I want using
              > the Lazy module:
              >
              > let x dir:string =
              > let cd = lazy (* Unix system call that cd's into dir *)
              > and mkdir = lazy (* Unix system call that makes dir *)
              > in
              > force mkdir;
              > force cd
              > ;;
              >
              > But I can also wrap cp and mkdir into anonymous functions and achieve
              > the same effect:
              > let x dir:string =
              > let cd = (fun () -> (* Unix system call that cd's into dir *))
              > and mkdir = (fun () -> (* Unix system call that makes dir *))
              > in
              > mkdir ();
              > cd ();
              > ;;
              >
              > Is there a difference between the approaches in terms of efficiency,
              > style etc? I find both to be equally readable.

              Using functions is more efficient because the result is not memoized. Also,
              you can write it more concisely:

              let x dir =
              let mkdir() = (* Unix system call that makes dir *) in
              let cd() = (* Unix system call that cd's into dir *) in
              mkdir();
              cd();

              You should using "and" unless it is necessary. Your type annotation is almost
              certainly superfluous.

              --
              Dr Jon D Harrop, Flying Frog Consultancy Ltd.
              http://www.ffconsultancy.com/products/?e
            Your message has been successfully submitted and would be delivered to recipients shortly.