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

Re[2]: "ocaml_beginners"::[] Choosing the order of parameters

Expand Messages
  • dmitry grebeniuk
    Hello, Peng. PZ Apropo partial application, I didn t think ocaml PZ did partial application... I mean you can always PZ give a function fewer arguments
    Message 1 of 18 , Sep 30, 2007
      Hello, Peng.

      PZ> Apropo partial application, I didn't think ocaml
      PZ> did partial application... I mean you can always
      PZ> give a function fewer arguments than it takes and
      PZ> ocaml will return a function with the remaining
      PZ> arguments but I didn't think ocaml did any thing
      PZ> (ie. evaluation/calculation) with the arguments
      PZ> that it has gotten. I thought it just held the
      PZ> arguments it has until it has all the arguments
      PZ> at which point, the function is then evaluated.

      That's not totally right. Try to run the following
      code and get the difference between partial applications:

      ======================================================
      let func1 () () =
      print_string "func1: fully applied\n";;
      let func2 () =
      let () = print_string "func2: partially applied\n" in
      fun () -> print_string "func2: fully applied\n";;
      let _ =
      print_string "1\n";
      let p1 = func1 () in
      print_string "2\n";
      p1 ();
      print_string "3\n";
      let p2 = func2 () in
      print_string "4\n";
      p2 ();
      print_string "5\n";;
      ======================================================

      Sometimes this difference is useful, it's like a
      "poor men's OOP": you can do some init-stuff when
      partially applying first argument(s), and then use
      resulting closure.
      But it's not a good style of programming, imho.

      --
      WBR,
      dmitry mailto:gds-mlsts@...
    • Peng Zang
      ... Hash: SHA1 Ahh, right. So in func2 the function is explictly defined in two parts and so there is actual partial application. I thought about why ocaml
      Message 2 of 18 , Oct 1, 2007
        -----BEGIN PGP SIGNED MESSAGE-----
        Hash: SHA1

        Ahh, right. So in func2 the function is explictly defined in two parts and so
        there is actual partial application. I thought about why ocaml doesn't do
        this by default but it makes sense as then the timing of any side-effects in
        your functions would be ambiguous. Thanks for the example,

        Peng

        On Monday 01 October 2007 02:35, dmitry grebeniuk wrote:
        > Hello, Peng.
        >
        > PZ> Apropo partial application, I didn't think ocaml
        > PZ> did partial application... I mean you can always
        > PZ> give a function fewer arguments than it takes and
        > PZ> ocaml will return a function with the remaining
        > PZ> arguments but I didn't think ocaml did any thing
        > PZ> (ie. evaluation/calculation) with the arguments
        > PZ> that it has gotten. I thought it just held the
        > PZ> arguments it has until it has all the arguments
        > PZ> at which point, the function is then evaluated.
        >
        > That's not totally right. Try to run the following
        > code and get the difference between partial applications:
        >
        > ======================================================
        > let func1 () () =
        > print_string "func1: fully applied\n";;
        > let func2 () =
        > let () = print_string "func2: partially applied\n" in
        > fun () -> print_string "func2: fully applied\n";;
        > let _ =
        > print_string "1\n";
        > let p1 = func1 () in
        > print_string "2\n";
        > p1 ();
        > print_string "3\n";
        > let p2 = func2 () in
        > print_string "4\n";
        > p2 ();
        > print_string "5\n";;
        > ======================================================
        >
        > Sometimes this difference is useful, it's like a
        > "poor men's OOP": you can do some init-stuff when
        > partially applying first argument(s), and then use
        > resulting closure.
        > But it's not a good style of programming, imho.
        -----BEGIN PGP SIGNATURE-----
        Version: GnuPG v2.0.2 (GNU/Linux)

        iD8DBQFHANmofIRcEFL/JewRAovCAKCUrRlWKH9VvXaBDb7PipaQc48UcgCgt+Pd
        ISkPFRf40SS2qB24kbHBHnE=
        =Cwrw
        -----END PGP SIGNATURE-----
      • Fabrice Marchant
        Thanks for these useful explanations and sorry for the delay ! ... instead of : let size = flip (M.fold (fun _ _ n - n + 1)) 0 Here the extra call to flip ,
        Message 3 of 18 , Oct 1, 2007
          Thanks for these useful explanations and sorry for the delay !

          Martin :
          > Anyway, choose the order of arguments that is the most standard, if
          > applicable, or that makes partial application easier. But you'll always
          > have to write things like (fun x -> f x y) instead of (f y). This is not
          > too bad.

          Jon :
          > # module M = Map.Make(String);;
          >...
          > # let size m = M.fold (fun _ _ n -> n + 1) m 0;;

          instead of :
          let size = flip (M.fold (fun _ _ n -> n + 1)) 0

          Here the extra call to 'flip', that avoids to express a parameter, will
          probably slow down things a bit.

          -------------------------------------------------------------------

          Martin :
          > Note that labelled arguments, optional or not, are very useful when you
          > have a lot of parameters, especially if several of them have the same
          > type.

          Peng :
          > In fact, I use it all the time as many of my functions use labelled arguments
          > (it's convenient as then the type sig for the function yields more semantic
          > meaning).

          A bit like C programmers sometimes name parameters in function declarations
          inside their .h files to make things easier to grasp ?

          ------------------------------------------------------------------------------

          >> However what general recommendation should we follow, at design time, to
          >> choose the parameter order ? Maybe trying to imagine what could be the most
          >> "natural" way to use the function. But what does it exactly mean ?

          Jon :
          > If you're writing standard functions like "map" and "fold" then you
          > should adhere to the conventions. If you're writing your own custom
          > functions then try to rearrange the order of the arguments to maximize
          > the usefulness of currying, i.e. put the arguments that are most likely
          > to be partially applied first.
          Thanks a lot Jon !

          ------------------------------------------------------------------------
          Regards,

          Fabrice
        • Michael Feathers
          let assert_ints_equal expected actual = if expected = actual then () else raise (TestFailure (sprintf expected: %d but was: %d expected actual)) let
          Message 4 of 18 , Oct 2, 2007
            let assert_ints_equal expected actual =
            if expected = actual then
            ()
            else
            raise (TestFailure (sprintf "expected: %d but was: %d" expected actual))

            let assert_strings_equal expected actual =
            if expected = actual then
            ()
            else
            raise (TestFailure (sprintf "expected: %s but was: %s" expected actual))

            assert_ints_equal 20 20;
            assert_strings_equal "1" "1"

            Can anyone tell me how do I make this nicer? If I understand the type
            system correctly, I do need two separate methods because of the
            sprintf. Is there a better way?

            Thanks,

            Michael
          • Martin Jambon
            ... Right. ... What you can do is pass a to_string function as a parameter: let assert_equal to_string expected actual = if expected = actual then () else
            Message 5 of 18 , Oct 2, 2007
              On Tue, 2 Oct 2007, Michael Feathers wrote:

              > let assert_ints_equal expected actual =
              > if expected = actual then
              > ()
              > else
              > raise (TestFailure (sprintf "expected: %d but was: %d" expected actual))
              >
              > let assert_strings_equal expected actual =
              > if expected = actual then
              > ()
              > else
              > raise (TestFailure (sprintf "expected: %s but was: %s" expected actual))
              >
              > assert_ints_equal 20 20;
              > assert_strings_equal "1" "1"
              >
              > Can anyone tell me how do I make this nicer? If I understand the type
              > system correctly, I do need two separate methods because of the
              > sprintf.

              Right.

              > Is there a better way?

              What you can do is pass a to_string function as a parameter:

              let assert_equal to_string expected actual =
              if expected = actual then
              ()
              else
              raise (TestFailure (sprintf "expected: %s but was: %s"
              (to_string expected) (to_string actual)))

              let assert_strings_equal e a = assert_equal (fun s -> s) e a
              let assert_ints_equal e a = assert_equal string_of_int e a
              ...

              Or even better:

              let assert_strings_equal = assert_equal (fun s -> s)
              let assert_ints_equal = assert_equal string_of_int



              Martin

              --
              http://martin.jambon.free.fr
            • Michael Feathers
              Martin, Thanks. That helps alot. Michael
              Message 6 of 18 , Oct 3, 2007
                Martin,

                Thanks. That helps alot.

                Michael

                Martin Jambon wrote:

                >On Tue, 2 Oct 2007, Michael Feathers wrote:
                >
                >
                >
                >>let assert_ints_equal expected actual =
                >> if expected = actual then
                >> ()
                >> else
                >> raise (TestFailure (sprintf "expected: %d but was: %d" expected actual))
                >>
                >>let assert_strings_equal expected actual =
                >> if expected = actual then
                >> ()
                >> else
                >> raise (TestFailure (sprintf "expected: %s but was: %s" expected actual))
                >>
                >>assert_ints_equal 20 20;
                >>assert_strings_equal "1" "1"
                >>
                >>Can anyone tell me how do I make this nicer? If I understand the type
                >>system correctly, I do need two separate methods because of the
                >>sprintf.
                >>
                >>
                >
                >Right.
                >
                >
                >
                >>Is there a better way?
                >>
                >>
                >
                >What you can do is pass a to_string function as a parameter:
                >
                >let assert_equal to_string expected actual =
                > if expected = actual then
                > ()
                > else
                > raise (TestFailure (sprintf "expected: %s but was: %s"
                > (to_string expected) (to_string actual)))
                >
                >let assert_strings_equal e a = assert_equal (fun s -> s) e a
                >let assert_ints_equal e a = assert_equal string_of_int e a
                >...
                >
                >Or even better:
                >
                >let assert_strings_equal = assert_equal (fun s -> s)
                >let assert_ints_equal = assert_equal string_of_int
                >
                >
                >
                >Martin
                >
                >--
                >http://martin.jambon.free.fr
                >
                >
                >
              • Michael Feathers
                I understand that it s poor practice to open modules, and that we re better off using explicit qualification, but I was wondering whether there are any
                Message 7 of 18 , Oct 3, 2007
                  I understand that it's poor practice to open modules, and that we're
                  better off using explicit qualification, but I was wondering whether
                  there are any disadvantages to opening List in a module? Do experienced
                  Ocamlers do this?

                  I do open Printf and it seems that there's nothing there that would
                  clash with anything. Some of the names in List are things I can imagine
                  clashing.. names like 'length', but on the other hand, it feels weird to
                  use explicit qualification for something as elemental as lists in a
                  functional language.

                  What is accepted practice on this?

                  Michael
                • Jon Harrop
                  ... You might get confused if you open List and String and try to use a function that is in both, like length . ... Yes. ... Indeed, I wonder why Printf isn t
                  Message 8 of 18 , Oct 3, 2007
                    On Thursday 04 October 2007 02:40:37 Michael Feathers wrote:
                    > I understand that it's poor practice to open modules, and that we're
                    > better off using explicit qualification, but I was wondering whether
                    > there are any disadvantages to opening List in a module?

                    You might get confused if you open List and String and try to use a function
                    that is in both, like "length".

                    > Do experienced Ocamlers do this?

                    Yes.

                    > I do open Printf and it seems that there's nothing there that would
                    > clash with anything.

                    Indeed, I wonder why Printf isn't in pervasives as it is in F#...

                    > Some of the names in List are things I can imagine
                    > clashing.. names like 'length', but on the other hand, it feels weird to
                    > use explicit qualification for something as elemental as lists in a
                    > functional language.
                    >
                    > What is accepted practice on this?

                    Make compilation units small and "open List" in ones where it helps
                    significantly.

                    --
                    Dr Jon D Harrop, Flying Frog Consultancy Ltd.
                    http://www.ffconsultancy.com/products/?e
                  • Brian Hurt
                    ... Because printf was a bad idea in C. Basically, you have a language within a language- and worse yet, it s a language within a string within a language.
                    Message 9 of 18 , Oct 3, 2007
                      On Thu, 4 Oct 2007, Jon Harrop wrote:

                      > Indeed, I wonder why Printf isn't in pervasives as it is in F#...

                      Because printf was a bad idea in C. Basically, you have a language within
                      a language- and worse yet, it's a language within a string within a
                      language. So your language- be it C, Ocaml, or F#, now needs to be able
                      to parse both the nominal language and this special "printf" language. In
                      addition, you can (in C, at least) dynamically construct format strings
                      from peicing together other strings, and thus totally obscure from the
                      compiler any ability to analyze this DSL at all.

                      I note that even C++ ditched printf, in favor of iostreams. Which, modulo
                      some bad implementation choices, I think is a better i/o library than
                      printf. Yes, try not to faint- Brian Hurt, who hates C++ with a bitter
                      passion and loves Ocaml, thinks that maybe C++ did something better than
                      Ocaml.

                      Brian
                    • Dave Benjamin
                      ... I used to open List, but no longer do this. I appreciate the brevity, but I don t like the asymetry between iter and Array.iter , for example. Many
                      Message 10 of 18 , Oct 4, 2007
                        On Wed, 3 Oct 2007, Michael Feathers wrote:

                        > I understand that it's poor practice to open modules, and that we're
                        > better off using explicit qualification, but I was wondering whether
                        > there are any disadvantages to opening List in a module? Do experienced
                        > Ocamlers do this?

                        I used to open List, but no longer do this. I appreciate the brevity, but
                        I don't like the asymetry between "iter" and "Array.iter", for example.
                        Many modules expect that you will qualify them, and this is reflected in
                        the names of functions they export, such as "Array.of_list". It'd be
                        pretty odd to use "of_list" by itself, no? However, unlike many standard
                        modules, List's methods seem to all make sense on their own, so perhaps it
                        was intended that some people might open it.

                        When writing code, I like the brevity of unqualified names. However, when
                        reading code (other people's code, especially), qualified names are nice
                        because I don't have to do so much work to figure out where a name comes
                        from. So, out of consideration, I tend to avoid "open" as much as I can.

                        One benefit of "open", though, is that it can serve to document the
                        dependencies of a module, somewhat like the "import" statements in other
                        languages. However, the modules really have to be designed with this in
                        mind, and many aren't.

                        > I do open Printf and it seems that there's nothing there that would
                        > clash with anything. Some of the names in List are things I can imagine
                        > clashing.. names like 'length', but on the other hand, it feels weird to
                        > use explicit qualification for something as elemental as lists in a
                        > functional language.

                        I open Printf too. Printf.sprintf is just too redundant, and I use it far
                        too often to qualify it every time. However, this is just about the only
                        module I open.

                        > What is accepted practice on this?

                        I can't speak for everyone, but in general your understanding is correct;
                        it's considered bad for to open modules, except for those (rare) cases
                        where the module is designed to be opened.

                        It would be nice if something like pa_openin was added to the standard
                        syntax. I find it hard to justify a syntax extension for something so
                        trivial, though. Occasionally, I simulate a local open with a "let module"
                        expression:

                        let module M = struct
                        open AModule
                        let v = unqualified_f x
                        end in
                        M.v

                        This can be particularly handy for record types.

                        Cheers,
                        Dave
                      Your message has been successfully submitted and would be delivered to recipients shortly.