Python from Wise Guy's Viewpoint

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Pascal Costanza

    Re: Python from Wise Guy's Viewpoint

    Fergus Henderson wrote:
    [color=blue]
    > Pascal Costanza <costanza@web.d e> writes:
    >
    >[color=green]
    >>- The important thing here is that the EMLOYED mixin works on any class,
    >>even one that is added later on to a running program. So even if you
    >>want to hire martians some time in the future you can still do this.[/color]
    >
    >
    > What happens if the existing class defines a slot named "salary" or "company",
    > but with a different meaning? Are slot names global, or is there some sort
    > of namespace control to prevent this kind of accidental name capture?[/color]

    Sure, that's what Common Lisp's packages are there for. Define the
    EMPLOYED mixin in its own package - done! You won't ever need to worry
    about name clashes.
    [color=blue]
    > Anyway, regarding how to write this example in a statically typed
    > language: you can do this in a quite straight-forward manner,
    > by just keeping a separate table of employees.[/color]

    Yuck.
    [color=blue]
    > static void test_employed() {
    > class Person {
    > public String name;
    > Person(String n) { name = n; }
    > };
    > Person joe = new Person("joe");
    > System.out.prin tln("-> hire joe");
    > hire(joe, 60000);
    > System.out.prin tln("name: " + joe.name);
    > System.out.prin tln("class: "
    > + joe.getClass(). getName());
    > Employee e = (Employee) employees.get(j oe);[/color]
    ^^^^^^^^^^^^^^^ ^^^
    This part is not domain-specific, but shows that your abstraction leaks.
    I.e. the client of your interface has to remember how the employee
    abstraction is implemented in order to use it correctly.

    In my original example, I was able to just call (company joe) and
    (salary joe) (or, in Java syntax, this would be joe.salary and
    joe.company). I.e., I don't have to know anything about the internal
    implementation.

    You can't implement unanticipated optional features in a statically
    typed language that doesn't involve leaking abstractions.
    [color=blue]
    > As you can see, there's no need here for dynamically changing the types of
    > objects at runtime or for creating classes at runtime. But you can employ
    > Martians or any other object.[/color]

    Sure. You also don't need functions and parameter passing. You also
    don't need GOSUB and RETURN. So why don't we just program in assembler
    again?
    [color=blue]
    > This example makes use of one dynamic cast; that's because the Java
    > type system doesn't support generics / parametric polymorphism. It would
    > be a little nicer to do this in a language which supported generics, then
    > we could use `Hashtable<Obje ct, Employee>' rather than just `Hashtable',
    > and there wouldn't be any need for the dynamic cast to `(Employee)'.[/color]

    I would still need to remember what features happen to be kept external
    from my objects and what not.


    Pascal

    P.S.: Your implementation of default_company doesn't match mine. Yours
    is not dynamically scoped. But maybe that's nitpicking...

    Comment

    • Fergus Henderson

      Re: Python from Wise Guy's Viewpoint

      Fergus Henderson <fjh@cs.mu.oz.a u> writes:
      [color=blue]
      >Anyway, regarding how to write this example in a statically typed
      >language: you can do this in a quite straight-forward manner,
      >by just keeping a separate table of employees.
      >For example, here it is in Java.[/color]

      And in case you didn't like the type declarations and downcast that you
      need in Java, here it is in Mercury.

      :- module employed.
      :- interface.
      :- import_module io.

      :- pred main(io::di, io::uo) is det.

      :- implementation.
      :- import_module map, string, std_util.

      default_company = "constanz-inc".

      :- type employee ---> some [Obj]
      employee(object ::Obj, salary::int, company::string ).

      hire(Obj, Salary, !Employees) :-
      hire(Obj, Salary, default_company , !Employees).
      hire(Obj, Salary, Company, !Employees) :-
      set(!.Employees , Obj, 'new employee'(Obj, Salary, Company),
      !:Employees).
      fire(Obj, !Employees) :-
      delete(!.Employ ees, Obj, !:Employees).

      :- type person ---> person(name::st ring).

      test_employed(! .Employees) -->
      { Joe = person("joe") },
      print("-> hire joe"), nl,
      { hire(Joe, 60000, !Employees) },
      print("name: " ++ Joe^name), nl,
      print("class: " ++ type_name(type_ of(Joe))), nl,
      print("employed : " ++ (if !.Employees `contains` Joe
      then "yes" else "no")), nl,
      print("company: " ++
      !.Employees^det _elem(Joe)^comp any), nl,
      print("salary: "),
      print(!.Employe es^det_elem(Joe )^salary), nl,
      print("-> fire joe"), nl,
      { fire(Joe, !Employees) },
      (if {!.Employees `contains` Joe} then
      print("joe is still employed."), nl
      else
      print("joe is not employed anymore."), nl
      ).

      main --> { init(Employees) }, test_employed(E mployees).

      --
      Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
      The University of Melbourne | of excellence is a lethal habit"
      WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

      Comment

      • Pascal Costanza

        Re: Python from Wise Guy's Viewpoint

        Fergus Henderson wrote:
        [color=blue]
        > Fergus Henderson <fjh@cs.mu.oz.a u> writes:
        >
        >[color=green]
        >>Anyway, regarding how to write this example in a statically typed
        >>language: you can do this in a quite straight-forward manner,
        >>by just keeping a separate table of employees.
        >>For example, here it is in Java.[/color]
        >
        >
        > And in case you didn't like the type declarations and downcast that you
        > need in Java, here it is in Mercury.[/color]

        Thanks for the variations on this theme. However, your abstraction is
        still leaking.
        [color=blue]
        > print("employed : " ++ (if !.Employees `contains` Joe[/color]
        ^^^^^^^^^^^^^^^ ^^^^^^^[color=blue]
        > then "yes" else "no")), nl,
        > print("company: " ++
        > !.Employees^det _elem(Joe)^comp any), nl,[/color]
        ^^^^^^^^^^^^^^^ ^^^^^^^^^^[color=blue]
        > print("salary: "),
        > print(!.Employe es^det_elem(Joe )^salary), nl,[/color]
        ^^^^^^^^^^^^^^^ ^^^^^^^^^^

        Pascal

        Comment

        • Fergus Henderson

          Re: Python from Wise Guy's Viewpoint

          Pascal Costanza <costanza@web.d e> writes:
          [color=blue]
          >Fergus Henderson wrote:
          >[color=green]
          >> static void test_employed() {
          >> class Person {
          >> public String name;
          >> Person(String n) { name = n; }
          >> };
          >> Person joe = new Person("joe");
          >> System.out.prin tln("-> hire joe");
          >> hire(joe, 60000);
          >> System.out.prin tln("name: " + joe.name);
          >> System.out.prin tln("class: "
          >> + joe.getClass(). getName());
          >> Employee e = (Employee) employees.get(j oe);[/color]
          > ^^^^^^^^^^^^^^^ ^^^
          >This part is not domain-specific, but shows that your abstraction leaks.
          >I.e. the client of your interface has to remember how the employee
          >abstraction is implemented in order to use it correctly.
          >
          >In my original example, I was able to just call (company joe) and
          >(salary joe) (or, in Java syntax, this would be joe.salary and
          >joe.company) . I.e., I don't have to know anything about the internal
          >implementation .[/color]

          Well, you have a point, I didn't encapsulate the use of the Hashtable.
          I can do that quite easily:

          static Employee employee(Object obj) {
          return (Employee) employees.get(o bj);
          }

          Then the code there could become as follows.

          System.out.prin tln("employed: " +
          (employee(joe) != null ? "yes" : "no"));
          System.out.prin tln("company: " + employee(joe).c ompany);
          System.out.prin tln("salary: " + employee(joe).s alary);

          If you prefer, you can make make it simpler still,

          System.out.prin tln("employed: " +
          (employeed(joe) ? "yes" : "no"));
          System.out.prin tln("company: " + company(joe));
          System.out.prin tln("salary: " + salary(joe));

          by defining suitable methods:

          static bool employed(Object obj) {
          return employee(obj) != null;
          }
          static String company(Object obj) {
          return employee(obj).c ompany;
          }
          static int salary(Object obj) {
          return employee(obj).s alary;
          }

          Now, my guess is that you're still going to be complaining about the
          abstraction leaking, but I don't think such complaints are valid.
          Yes, the syntax is different than a field access, but that's not
          important. The client is going to need to know the names of the
          attributes or methods they want to use anyway; as long as they need to
          know whether the name of the entity is "wage" or "salary", it doesn't make
          a significant difference that they also need to know whether the interface
          to that entity is a field, a member function, or a static function.

          And of course, this syntax issue is language-specific. In Mercury
          and Haskell, the same syntax is used for field access, method call,
          and function call, so the issue doesn't arise!
          [color=blue]
          >You can't implement unanticipated optional features in a statically
          >typed language that doesn't involve leaking abstractions.[/color]

          Not true. See above.
          [color=blue]
          >I would still need to remember what features happen to be kept external
          >from my objects and what not.[/color]

          No, that's not what you need to remember. You just need to remember the
          names of the features, and for each feature what kind of feature it is:
          whether it is a field, an instance method, or a static method.
          If it is a field, you know it is kept inside the object, but in
          the other two cases the implementation is encapsulated -- the user
          can't tell where the data is stored.

          --
          Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
          The University of Melbourne | of excellence is a lethal habit"
          WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

          Comment

          • Dirk Thierbach

            Re: Python from Wise Guy's Viewpoint

            prunesquallor@c omcast.net wrote:[color=blue]
            > Joachim Durchholz <joachim.durchh olz@web.de> writes:[/color]
            [color=blue][color=green]
            >> I'm still hoping for an explanation on the practical relevance of that
            >> black hole device.[/color][/color]
            [color=blue]
            > Neel Krishnaswami had a wonderful explanation in article
            > <slrnbplpv6.ikr .neelk@gs3106.s p.cs.cmu.edu>[/color]

            And note that his example for practical relevance includes a datatype.

            Haskell doesn't support recursive types directly, but a recursive
            datatype for lists is easy:

            data List a = Nil | Cons a (List a)

            Since Haskell is lazy, that's already a lazy list, but even if Haskell
            was eager, you could write a similar datatype for lazy lists.
            (And of course Haskell lists are essentially just given by the above
            datatype, together with some syntactic sugar).

            It's very natural for a relevant application of recursive types to
            be coupled with data.

            - Dirk




            Comment

            • CezaryB

              Re: Python from Wise Guy's Viewpoint

              On 10/20/2003 5:49 AM, Kenny Tilton wrote:[color=blue]
              >
              >
              > Dennis Lee Bieber wrote:
              >[color=green]
              >> Just check the archives for comp.lang.ada and Ariane-5.
              >>
              >> Short version: The software performed correctly, to
              >> specification (including the failure mode) -- ON THE ARIANE 4 FOR
              >> WHICH IT WAS DESIGNED.[/color]
              >
              >
              > Nonsense. From: http://www.sp.ph.ic.ac.uk/Cluster/report.html
              >
              > "The internal SRI software exception was caused during execution of a
              > data conversion from 64-bit floating point to 16-bit signed integer[/color]
              [...]

              [color=blue]
              >
              >[color=green]
              >> LISP wouldn't have helped -- since the A-4 code was supposed
              >> to failure with values that large... And would have done the same
              >> thing if plugged in the A-5. (Or are you proposing that the A-4 code
              >> is supposed to ignore a performance requirement?)[/color]
              >
              > "supposed to" fail? chya. This was nothing more than an unhandled
              > exception crashing the sytem and its identical backup. Other conversions
              > were protected so they could handle things intelligently, this bad boy
              > went unguarded. Note also that the code functionality was pre-ignition
              > only, so there is no way they were thinking that a cool way to abort the
              > flight would be to leave a program exception unhandled.
              >
              > What happened (aside from an unnecessary chunk of code running
              > increasing risk to no good end) is that the extra power of the A5 caused
              > oscillations greater than those seen in the A4. Those greater
              > oscillations took the 64-bit float beyond what would fit in the 16-bit
              > int. kablam. Operand Error. This is not a system saying "whoa, out of
              > range, abort".[/color]


              "To determine the vulnerability of unprotected code, an analysis was performed
              on every operation which could give rise to an exception, including an Operand
              Error. [...] It is important to note that the decision to protect certain
              variables but not others was taken jointly by project partners at several
              contractual levels."

              "There is no evidence that any trajectory data were used to analyse the
              behaviour of the unprotected variables, and it is even more important to note
              that it was jointly agreed not to include the Ariane 5 trajectory data in the
              SRI requirements and specification."


              "It was the decision to cease the processor operation which finally proved
              fatal. Restart is not feasible since attitude is too difficult to re-calculate
              after a processor shutdown; therefore the Inertial Reference System becomes
              useless. The reason behind this drastic action lies in the culture within the
              Ariane programme of only addressing random hardware failures. From this point of
              view exception - or error - handling mechanisms are designed for a random
              hardware failure which can quite rationally be handled by a backup system."


              [color=blue]
              > As for Lisp not helping:[/color]

              "It has been stated to the Board that not all the conversions were protected
              because a maximum workload target of 80% had been set for the SRI computer"



              CB


              Comment

              • Fergus Henderson

                Re: Python from Wise Guy's Viewpoint

                Pascal Costanza <costanza@web.d e> writes:
                [color=blue]
                >OK, let's try to distill this to some simple questions.
                >
                >Assume you have a compiler ML->CL that translates an arbitrary ML
                >program with a main function into Common Lisp. The main function is a
                >distinguishe d function that starts the program (similar to main in C).
                >The result is a Common Lisp program that behaves exactly like its ML
                >counterpart, including the fact that it doesn't throw any type errors at
                >runtime.
                >
                >Assume furthermore that ML->CL retains the explicit type annotations in
                >the result of the translation in the form of comments, so that another
                >compiler CL->ML can fully reconstruct the original ML program without
                >manual help.
                >
                >Now we can modify the result of ML->CL for any ML program as follows. We
                >add a new function that is defined as follows:
                >
                >(defun new-main ()
                > (loop (print (eval (read)))))
                >
                >(We assume that NEW-MAIN is a name that isn't defined in the rest of the
                >original program. Otherwise, it's easy to automatically generate a
                >different unique name.)
                >
                >Note that we haven't written an interpreter/compiler by ourselves here,
                >we just use what the language offers by default.
                >
                >Furthermore, we add the following to the program: We write a function
                >RUN (again a unique name) that spawns two threads. The first thread
                >starts the original main function, the second thread opens a console
                >window and starts NEW-MAIN.
                >
                >Now, RUN is a function that executes the original ML program (as
                >translated by ML->CL, with the same semantics, including the fact that
                >it doesn't throw any runtime type errors in its form as generated by
                >ML->CL), but furthermore executes a read-eval-print-loop that allows
                >modification of the internals of that original program in arbitrary
                >ways. For example, the console allows you to use DEFUN to redefine an
                >arbitrary function of the original program that runs in the first
                >thread, so that the original definition is not visible anymore and all
                >calls to the original definiton within the first thread use the new
                >definition after the redefinition is completed. [1]
                >
                >Now here come the questions.
                >
                >Is it possible to modify CL->ML in a way that any program originally
                >written in ML, translated with ML->CL, and then modified as sketched
                >above (including NEW-MAIN and RUN) can be translated back to ML?[/color]

                Yes, it is possible. For example, have CL->ML ignore the type annotation
                comments, and translate CL values into a single ML discriminated union type
                that can represent all CL values. Or (better, but not quite the same semantics)
                translate those CL values with ML type annotations back to corresponding ML
                types, and insert conversions to convert between the generic CL type and
                other specific types where appropriate.

                Suppose the original ML program defines the following functions

                foo : int -> int
                bar : string -> string
                ...

                We can add dynamic typing like this:

                datatype Generic = Int of int
                | String of string
                | Atom of string
                | Cons Generic Generic
                | Apply Generic Generic
                | ...

                fun foo_wrapper (Int x) = (Int (foo x))

                fun bar_wrapper (String x) (String (foo x))

                and then dynamic binding like this

                val foo_binding = ref foo_wrapper

                val bar_binding = ref bar_wrapper

                For dynamic binding, function calls will have to be translated to does
                an indirection. So a call

                let y = foo x

                will become

                let y = !foo_binding x

                or, if x and y are used in a way that requires that they have type int,
                then

                let (Int y) = !foo_binding (Int x)

                We can then simulate eval using an explicit symbol table.

                fun lookup "foo" = !foo_binding
                | lookup "bar" = !bar_binding
                | lookup "define" = !define_binding
                ...

                fun define "foo" f = foo_binding := f
                | define "bar" f = bar_binding := f
                | define "define" f = define_binding := f
                ...

                fun define_wrapper (Cons (Atom name) body) =
                let () = define name body in Atom "()"
                val define_binding = ref define_wrapper

                fun eval (Apply func arg) =
                case (eval func) of
                Atom funcname => (lookup funcname) (eval arg)
                | eval x = x

                Note that our symbol table includes an entry for the function "define",
                so that eval can be used to modify the dynamic bindings.

                The rest (e.g. read) is straight-forward.
                [color=blue]
                >To ask the question in more detail:
                >
                >a) Is it possible to write CL->ML in a way that the result is both still
                >statically type checkable[/color]

                Yes, but only in a weak sense. Since we are allowing dynamic binding,
                and we want to be able to dynamically bind symbols to a different type,
                we're definitely going to have the possibility of dynamic type errors.
                The way this is resolved is that things which would have been type errors
                in the original ML program may become data errors (invalid constructor
                in an algebraic type Generic) in the final ML program.
                [color=blue]
                >and not considerably larger than the original program that was given to
                >ML->CL.[/color]

                There's a little bit of overhead for the wrapper functions of type
                "Generic -> Generic", and for the variables of type "ref (Generic -> Generic)"
                which store the dynamic bindings. It's about two lines of code per
                function in the original program. I don't think this is excessive.
                [color=blue]
                >Especially, is it possible to do this
                >without implementing a new interpreter/compiler on top of ML and then
                >letting the program run in that language, but keep the program
                >executable in ML itself?[/color]

                The program includes a definition for "eval", and "eval" is an
                interpreter, So in that sense, we have added a new interpreter.
                But the bulk of the program is written in ML, without making use of eval.
                [color=blue]
                >c) If you respond with yes to either a or b, what does your sketch of an
                >informal proof in your head look like that convincingly shows that this
                >can actually work?[/color]

                See above.

                --
                Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
                The University of Melbourne | of excellence is a lethal habit"
                WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

                Comment

                • Pascal Costanza

                  Re: Python from Wise Guy's Viewpoint

                  Fergus Henderson wrote:
                  [color=blue]
                  > Suppose the original ML program defines the following functions
                  >
                  > foo : int -> int
                  > bar : string -> string
                  > ...
                  >
                  > We can add dynamic typing like this:
                  >
                  > datatype Generic = Int of int
                  > | String of string
                  > | Atom of string
                  > | Cons Generic Generic
                  > | Apply Generic Generic
                  > | ...[/color]
                  ^^^
                  How many do you need of those, especially when you want to allow that
                  type to be extended in the running program?
                  [color=blue]
                  > Note that our symbol table includes an entry for the function "define",
                  > so that eval can be used to modify the dynamic bindings.[/color]

                  DEFUN is just one example. What about DEFTYPE, DEFCLASS, DEFPACKAGE, and
                  so forth...
                  [color=blue]
                  > The program includes a definition for "eval", and "eval" is an
                  > interpreter, So in that sense, we have added a new interpreter.[/color]

                  That's the whole point of my argument.


                  Pascal

                  --
                  Pascal Costanza University of Bonn
                  mailto:costanza @web.de Institute of Computer Science III
                  http://www.pascalcostanza.de Römerstr. 164, D-53117 Bonn (Germany)

                  Comment

                  • Fergus Henderson

                    Re: Python from Wise Guy's Viewpoint

                    Pascal Costanza <costanza@web.d e> writes:
                    [color=blue][color=green]
                    >> The program includes a definition for "eval", and "eval" is an
                    >> interpreter, So in that sense, we have added a new interpreter.[/color]
                    >
                    >That's the whole point of my argument.[/color]

                    Then it's a pretty silly argument.

                    You ask if we can implement eval, which is an interpreter,
                    without including an interpreter?
                    I don't see what you could usefully conclude from the answer.

                    --
                    Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
                    The University of Melbourne | of excellence is a lethal habit"
                    WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

                    Comment

                    • Fergus Henderson

                      Re: Python from Wise Guy's Viewpoint

                      Pascal Costanza <costanza@web.d e> writes:
                      [color=blue]
                      >Fergus Henderson wrote:
                      >[color=green]
                      >> Suppose the original ML program defines the following functions
                      >>
                      >> foo : int -> int
                      >> bar : string -> string
                      >> ...
                      >>
                      >> We can add dynamic typing like this:
                      >>
                      >> datatype Generic = Int of int
                      >> | String of string
                      >> | Atom of string
                      >> | Cons Generic Generic
                      >> | Apply Generic Generic
                      >> | ...[/color]
                      > ^^^
                      >How many do you need of those, especially when you want to allow that
                      >type to be extended in the running program?[/color]

                      In Haskell you would just use "Dynamic" instead of the above, and be done
                      with it. In Mercury, you'd just use "univ".

                      In SML, I don't know. Probably none. In fact, the first two entries in
                      the type above are not really necessary, you could just represent ints
                      and strings as Atoms. Even Apply could be represented using just Cons
                      and Atom: instead of Apply x y, we could use Cons (Atom "apply") (Cons x y).
                      [color=blue][color=green]
                      >> Note that our symbol table includes an entry for the function "define",
                      >> so that eval can be used to modify the dynamic bindings.[/color]
                      >
                      >DEFUN is just one example. What about DEFTYPE, DEFCLASS, DEFPACKAGE, and
                      >so forth...[/color]

                      Well, DEFTYPE in lisp really just defines a function, doesn't it?
                      So that's not much different than DEFUN. Likewise, DEFCLASS just
                      defines a collection of functions, doesn't it? OK, maybe these
                      also record some extra information in some global symbol tables.
                      That is easily emulated if you really want.

                      I don't know exactly what DEFPACKAGE does, but if the others are any
                      guide, it's probably not too hard either.

                      --
                      Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
                      The University of Melbourne | of excellence is a lethal habit"
                      WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

                      Comment

                      • Pascal Costanza

                        Re: Python from Wise Guy's Viewpoint

                        Fergus Henderson wrote:[color=blue]
                        > Pascal Costanza <costanza@web.d e> writes:
                        >
                        >[color=green][color=darkred]
                        >>>The program includes a definition for "eval", and "eval" is an
                        >>>interprete r, So in that sense, we have added a new interpreter.[/color]
                        >>
                        >>That's the whole point of my argument.[/color]
                        >
                        >
                        > Then it's a pretty silly argument.
                        >
                        > You ask if we can implement eval, which is an interpreter,
                        > without including an interpreter?[/color]

                        Right.
                        [color=blue]
                        > I don't see what you could usefully conclude from the answer.[/color]

                        ....that you can't statically type check your code as soon as you
                        incorporate an interpreter/compiler into your program that can interact
                        with and change your program at runtime in arbitrary ways.


                        Pascal

                        --
                        Pascal Costanza University of Bonn
                        mailto:costanza @web.de Institute of Computer Science III
                        http://www.pascalcostanza.de Römerstr. 164, D-53117 Bonn (Germany)

                        Comment

                        • Pascal Costanza

                          Re: Python from Wise Guy's Viewpoint

                          Fergus Henderson wrote:[color=blue]
                          > Pascal Costanza <costanza@web.d e> writes:[/color]
                          [color=blue][color=green]
                          >>DEFUN is just one example. What about DEFTYPE, DEFCLASS, DEFPACKAGE, and
                          >>so forth...[/color]
                          >
                          > Well, DEFTYPE in lisp really just defines a function, doesn't it?
                          > So that's not much different than DEFUN. Likewise, DEFCLASS just
                          > defines a collection of functions, doesn't it? OK, maybe these
                          > also record some extra information in some global symbol tables.
                          > That is easily emulated if you really want.
                          >
                          > I don't know exactly what DEFPACKAGE does, but if the others are any
                          > guide, it's probably not too hard either.[/color]

                          I am not sure if I understand you correctly, but are you actually
                          suggesting that it is better to reimplement Common Lisp on your own than
                          to just use one of the various Common Lisp implementations ?

                          All I am trying to get across is that, in case you need the flexibility
                          a dynamic language provides by default, and only occasionally need to
                          restrict that flexibility, it's better to use a dynamic language from
                          the outset. The price to pay is that you cannot make use of a 100%
                          strict static type system anymore, but on the other hand you can pick
                          one of the stable Common Lisp implementations with language features
                          that have proven to work well during the past few decades.

                          Sure, if you don't need the flexibility of a dynamic language then you
                          can think about using a static language that might buy you some
                          advantages wrt to static checkability. But I highly doubt that this is a
                          rational choice because I don't know any empirical studies that show
                          that the problems that static languages intend to solve are in fact
                          problems that occur in practice.

                          Let's inspect the list of those problems again:

                          a) performance

                          Good Lisp/Scheme implementations don't have problems in this regard.

                          b) documentation

                          Documentation can be handled well with comments and well-chosen names.

                          c) absence of a certain class of bugs

                          It's not clear whether this class of bugs really occurs in practice.
                          There are also indications that these relatively trivial bugs are also
                          covered by test suites as soon as they consist of a reasonable number of
                          test cases.

                          d) unbreakable abstraction boundaries

                          Such boundaries seem to be as tedious to implement in dynamic languages,
                          as is the case for dynamic features in static languages.

                          My conclusions would be as follows:

                          a) and b) are relatively uninteresting. c) needs convincing studies from
                          the proponents of static type systems. As long as they don't provide
                          them, it's just guesswork that these bugs are really important.

                          d) is the hard part. Proponents of static languages say that it's
                          important to be able to express such boundaries because it increases
                          their expressive power. (That's one thing I have learned from this
                          discussion: It is arguable that this can also be an important kind of
                          expressive power.) Proponents of dynamic languages say that it's more
                          important to be able to work around restrictions, no matter whether they
                          are intentional or not; it's better not to be able to paint yourself
                          into a corner.

                          With regard to d, both "groups" don't have enough objective empirical
                          evidence beyond their own subjective experiences. Static programmers
                          don't have enough objective empirical evidence that their languages
                          objectively increase the quality of their software, and dynamic
                          programmers don't have enough objective empirical evidence that painting
                          oneself into corners is a problem that occurs regularly.

                          So both views essentially boil down to be no more than subjective belief
                          systems. And IMHO that's not so far from the "truth": I am convinced
                          that these issues depend mostly on personal programming style and
                          preferences and not so much on technological issues. Software quality is
                          a social construct, and social problems can hardly be solved with
                          technical means. If you have a bunch of good programmers and let them
                          choose their preferred tools, it's more likely that they produce good
                          software than when you have a bunch of average programmers and tell them
                          what tools they must use.

                          (It's very important in this regard that we are not talking about
                          braindead languages. There are both extremely stupid as well as
                          excellent exemplars of static and dynamic languages.)


                          Pascal

                          --
                          Pascal Costanza University of Bonn
                          mailto:costanza @web.de Institute of Computer Science III
                          http://www.pascalcostanza.de Römerstr. 164, D-53117 Bonn (Germany)

                          Comment

                          • Fergus Henderson

                            Re: Python from Wise Guy's Viewpoint

                            Pascal Costanza <costanza@web.d e> writes:[color=blue]
                            >I am not sure if I understand you correctly, but are you actually
                            >suggesting that it is better to reimplement Common Lisp on your own than
                            >to just use one of the various Common Lisp implementations ?[/color]

                            No. I'm just pointing out that these sort of things can be implemented
                            in statically typed languages without much difficulty. Generally
                            I _don't_ want to reimplement Common Lisp. If on occaision I do need
                            some dynamic binding, or dynamic typing, say, then this need is usually
                            localized within a small part of the application, and so I can implement
                            what I need very easily.

                            Your article that I was responding to was suggesting that there might
                            be some things which could not be done in statically typed languages,
                            and in particular that this sort of eval(read()) loop might be one of them.
                            As I hope I've demonstrated, it is not.

                            So in answer to question "d" in that post of yours, "would you still
                            disagree with the assessment there is a class of programs that can be
                            implemented with dynamically typed languages but without statically
                            typed ones?", I would say yes, I still disagree.

                            --
                            Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
                            The University of Melbourne | of excellence is a lethal habit"
                            WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

                            Comment

                            • Fergus Henderson

                              Re: Python from Wise Guy's Viewpoint

                              Pascal Costanza <costanza@web.d e> writes:
                              [color=blue]
                              >b) documentation
                              >
                              >Documentatio n can be handled well with comments and well-chosen names.[/color]

                              Certainly it _can_ be, but keeping good, up-to-date documentation is a
                              difficult task. In my experience, average programmers do a better job
                              of documentation in languages which encourage explicitly declaring the
                              types of interfaces than in those which do not.

                              --
                              Fergus Henderson <fjh@cs.mu.oz.a u> | "I have always known that the pursuit
                              The University of Melbourne | of excellence is a lethal habit"
                              WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.

                              Comment

                              • Jon S. Anthony

                                Re: Python from Wise Guy's Viewpoint

                                Fergus Henderson <fjh@cs.mu.oz.a u> writes:
                                [color=blue]
                                > Well, DEFTYPE in lisp really just defines a function, doesn't it?
                                > So that's not much different than DEFUN. Likewise, DEFCLASS just
                                > defines a collection of functions, doesn't it? OK, maybe these
                                > also record some extra information in some global symbol tables.
                                > That is easily emulated if you really want.
                                >
                                > I don't know exactly what DEFPACKAGE does, but if the others are any
                                > guide, it's probably not too hard either.[/color]

                                Yes, if you reimplement Lisp you can achieve what was asked. However,
                                I don't think "turing equivalence" or Greenspun's tenth was the point...

                                /Jon

                                Comment

                                Working...