PDA

View Full Version : Dynamic typing is a good thing?


Princess
13th September 2008, 10:42 PM
I am fascinated by the hacker culture, and I personally admit to being a fan of Paul Graham even though most people want to punch him in face after reading his essays. Consistently, without fail, I see hackers raving on and on about Ruby, Python, Perl, and Lisp as the greatest thing since the text editor, and booing languages like Java, C++, Delphi, and Cobol.

I'll admit that Java et. al. really do suck by comparison, but is Lisp and Ruby really the best we can do?

I've programmed in Python and Perl, and I personally find it amazing that hackers put these languages on such a high pedestal. The big thing that gets me about these languages that they are dynamically typed, rather than statically typed. For those who like definitions:

- Statically typed languages are those like C#, Java, and Delphi where the datatypes of variables and functions are known to the compiler at compile time. When you try to use a variable of type 'a where a variable of type 'b is expected, you get a compilation error.

- Dynamically typed languages are those like Python, Perl, Ruby, and Lisp where the datatypes of variables and functions are known at runtime. If you use the wrong type of variable in a function, you don't know about it until runtime.

From my point of view, type errors are so frequent and easy to make that deliberately choosing to write a program in a language that can't enforce type safety before runtime is like playing sports without a cup. Its just too easy to take one in the rocks, and when it happens, it hurts. A lot.

I think programmers like the dynamic languages above because, in general, type annotations are verbose, and languages like Ruby lend to remarkably abstract and concise programs (its easy to do a lot with little code).

I find it odd that a language like OCaml, F#, SML and other ML languages aren't as popular and mainstream as Python. ML languages are statically typed, but uses type inference to save programmers the chore of writing out type annotations. ML allows programmers write higher order functions that take and return other functions as values just as naturally and succinctly as hackers do in Lisp. You get all the speed of a compiled, statically typed language with all the brevity in the dynamic languages that hackers love, but you never find people batting these languages around in hacker circles because hardly anyone has ever heard of them.

The entire hacker culture is built around dynamic languages, and tons of authority will insist that Ruby, Python, Perl, and Lisp are the languages to learn (along with a smattering of C++) if you want to become a truly great hacker.

I just want to survey public opinion: are dynamically typed languages the way to go to become a great programmer? Would any of the dynamic languages above be better if they where statically typed like C# and Java?

Please share your thoughts :)

shadron
13th September 2008, 11:09 PM
Having lived through the weak typed/strong type/I thru N type days, I feel that strongly typed languages are the only way to go. I'm often amazed that javascript or VB or whatever other language du jour can tell what a variable is, or perhaps continually convert from one type to another, and (seemingly) get it right as often as they do. It amazes me that you *must* learn 6 ot 7 languages to be able to display data on a webpage, most of which are completely redundant and overlapping in purpose and intent, and yet so amazingly distinct in syntax. A current project I'm doing consists in using MS Access/VBA to create static web pages from an Access database, the pages containing HTML, CSS, Javascript and a little CGI in Perl and/or PHP. Just keeping the quoting, continuation and comment syntax straight can get to be a boggle.

PS - I'm not a hacker. Web work is not as much fun as stright-forward programming for me, but that's probably my age telling.

six7s
13th September 2008, 11:59 PM
are dynamically typed languages the way to go to become a great programmer? I guess it depends on how you define 'great programmer'...

From my perspective, great programmers write great code... and great code is code that is easy (as in simple) to read/maintain, is platform-independent (future-proof) and works, fast!

I seem to recall that un-typed (and loosely-typed?) vars are memory hogs... and, to me, compromising run-time speed in favour of saving a few seconds per var in development time seems stoopid

I, like shadron, ain't no hacker either... nor do I know any... at least in the sense I think you mean (the modern anarchic variety, as opposed to the originals; the trail blazers)

Instead, I prefer to associate with coders who write clean, fast, elegant, 'sexy' code...

headrush.typepad.com / creating_passionate_users / Code Like A Girl (http://headrush.typepad.com/creating_passionate_users/2006/03/code_like_a_gir.html)
Do engineers and programmers care about concepts like beauty and elegance? Should they? Designers have always known that looks matter--that the outside (interface) matters. But deep in the heart of those building the inside--the technology most users never see--lies the sensibility of an artist. In a kind of "Design Eye for the Code Guy" way.

<snip/>

What prompted this post--and it's whimsical title--is a post by Jamis Buck titled Beautiful code, test first (http://37signals.com/svn/archives2/beautiful_code_testfirst.php), which includes the following:
"He was telling me how he feels like he has to sit and tweak his code over and over until it not only acts right, but looks right. It cannot be merely functional, it must be beautiful, as well."

But the best part was a comment by "Morten" that included the line:

"As for spending too much time on making the code look right down to the last indentation - my code has been called “girl code” for the same reason..."

And there you have it. I think "girl code" is quite a compliment. Because caring about things like beauty makes us better programmers and engineers. We make better things. Things that aren't just functional, but easy to read, elegantly maintainable, easier--and more joyful--to use, and sometimes flat-out sexy. A passion for aesthetics can mean the difference between code that others enjoy working on vs. code that's stressful to look at. And whether we like it or not, most of the world associates an appreciation for beauty more with women than men (especially geek men). Women may have a genetic advantage here.
<snip/>

Curious to see Ruby mentioned in the replies:
Wonderful article. It's interesting to me that the Ruby community as a whole seems to have the "code like a girl/metrosexual" value as a whole, though I more associated with that "Japanese aesthetic" that seems to permeate through out.
<snip/>

When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong.
R. Buckminster Fuller (1895 - 1983)

Jekyll
14th September 2008, 06:43 AM
I am fascinated by the hacker culture, and I personally admit to being a fan of Paul Graham even though most people want to punch him in face after reading his essays. Consistently, without fail, I see hackers raving on and on about Ruby, Python, Perl, and Lisp as the greatest thing since the text editor, and booing languages like Java, C++, Delphi, and Cobol.

I'll admit that Java et. al. really do suck by comparison, but is Lisp and Ruby really the best we can do?

I've programmed in Python and Perl, and I personally find it amazing that hackers put these languages on such a high pedestal. The big thing that gets me about these languages that they are dynamically typed, rather than statically typed. For those who like definitions:

- Statically typed languages are those like C#, Java, and Delphi where the datatypes of variables and functions are known to the compiler at compile time. When you try to use a variable of type 'a where a variable of type 'b is expected, you get a compilation error.

- Dynamically typed languages are those like Python, Perl, Ruby, and Lisp where the datatypes of variables and functions are known at runtime. If you use the wrong type of variable in a function, you don't know about it until runtime.


Your definitions are wrong.

A statically typed language is one where a single variable can only take values of one type, while dynamically typed languages allow the type of the value to change.

In practice this means that compilers for dynamic languages are allowed to know the type of a variable at compile time, but may not always do so.

Good dynamic compilers, like SBCL or CMUCL for common lisp, will detect some of the bad typing at compile time and warn you that the code is broken before you try to execute it.

In the end there is relatively little difference between a compiler for a good statically typed language, like Haskell, and a good compiler for a dynamic typed language. Both need a minimum (often zero) number of type declarations to work out what is going on and infer the type of the remaining variables. The principle difference is that the statically typed compiler will reject code if it can not prove it correct, while the dynamic compiler can only reject code if it proves it is incorrect.

Trying to decide if statical typing is better than dynamic by looking at C++ and Ruby is futile. Neither compiler will perform type inference, and in this respect both languages can be considered to be the worst case for dynamic or static typing.

69dodge
14th September 2008, 10:12 PM
ML languages are statically typed, but uses type inference to save programmers the chore of writing out type annotations. ML allows programmers write higher order functions that take and return other functions as values just as naturally and succinctly as hackers do in Lisp.

I don't mean to take sides---actually, I'm more comfortable programming in C++ than Lisp, myself---but can you write this funny Scheme function in ML? I don't think so.

> (define (funny f)
(list (f 3) (f '(a b c))))

> (funny (lambda (x) x))
(3 (a b c))

> (funny list)
((3) ((a b c)))

> (funny (lambda (x) (cons 1 x)))
((1 . 3) (1 a b c))

I have no experience writing big Lisp programs, so I couldn't tell you exactly how this flexibility is useful in practice, but you can at least see from this small example that there is some extra flexibility.

Princess
19th September 2008, 10:22 PM
I don't mean to take sides---actually, I'm more comfortable programming in C++ than Lisp, myself---but can you write this funny Scheme function in ML? I don't think so.

> (define (funny f)
(list (f 3) (f '(a b c))))

> (funny (lambda (x) x))
(3 (a b c))

> (funny list)
((3) ((a b c)))

> (funny (lambda (x) (cons 1 x)))
((1 . 3) (1 a b c))

I have no experience writing big Lisp programs, so I couldn't tell you exactly how this flexibility is useful in practice, but you can at least see from this small example that there is some extra flexibility.

There's no ML equivalent, eh ;)

> let funny (f : obj -> obj) = [f 3; f ["a", "b", "c"]];;
val funny : (obj -> obj) -> obj list

> funny (fun x -> x);;
val it : obj list = [3; [("a", "b", "c")]]

> funny (fun x -> [x] :> obj);;
val it : obj list = [[3]; [[("a", "b", "c")]]]

> funny (fun x -> box 1 :: x :: [] :> obj);;
val it : obj list = [[1; 3]; [1; [("a", "b", "c")]]]

> let whatever (x : obj) =
match x with
| :? string -> "string"
| :? int -> "integer"
| _ -> "unknown type"

List.iter (fun x -> printfn "%s" (whatever x)) (funny (fun x -> x));;

val whatever : obj -> string

integer
unknown type

This is identical to your code.


For anyone interested, here's a free ML lesson:

Unlike Lisp, all objects in ML are strongly and statically typed. A verbatim conversion of the Lisp:
(define (funny f)
(list (f 3) (f '(a b c))))

To ML results in an error:
let funny f = [f 3; f ["a", "b", "c"]]

f 3 gives f the type (int -> 'a) (a function that takes an int, and returns a generic type 'a). As a result, the list has the type (int -> 'a) list.

f ["a", "b", "c"] has the type ((string * string * string) list -> 'a) (a function that takes a 3-tuple of strings and returns the same datatype as the previous function).

Since the list expects a type int but got a tuple list, its a type error -- at least with type inference.

Using some type annotations, I can coerce the function f to a different type if I really needed that functionality:
let funny (f : obj -> obj) = [f 3; f ["a", "b", "c"]]

Now I've thrown away my type safety. funny has the type (obj -> obj) list (a list of functions that take an a parameter of type obj and return a value of type obj)

As a result, I can write the following:

> funny (fun x -> x);;
val it : obj list = [3; [("a", "b", "c")]]

> funny (fun x -> [x] :> obj);;
val it : obj list = [[3]; [[("a", "b", "c")]]]

> funny (fun x -> box 1 :: x :: [] :> obj);;
val it : obj list = [[1; 3]; [1; [("a", "b", "c")]]]

Since ML doesn't perform implicit conversions, not even for base types, I have to explicitly cast the last lists in the last two functions to the base type obj.

Without the cast, the function has the return type (obj -> obj list), a completely different function signature from (obj -> obj) that funny requires.


This style of programming can be useful in a narrow, specific set of problems (like instantiating a collection of objects with an unknown interface, passing a bunch of parameters to a function that simply prints out or logs each parameter, inter op with some a third-party DLL that expects an array of objects).

Outside of those narrow cases, writing code like that in a ML environment is guaranteed to get you punched in the face.

Jekyll
20th September 2008, 07:31 AM
Now I've thrown away my type safety. funny has the type (obj -> obj) list (a list of functions that take an a parameter of type obj and return a value of type obj)

As a result, I can write the following:

> funny (fun x -> x);;
val it : obj list = [3; [("a", "b", "c")]]

> funny (fun x -> [x] :> obj);;
val it : obj list = [[3]; [[("a", "b", "c")]]]

> funny (fun x -> box 1 :: x :: [] :> obj);;
val it : obj list = [[1; 3]; [1; [("a", "b", "c")]]]

Since ML doesn't perform implicit conversions, not even for base types, I have to explicitly cast the last lists in the last two functions to the base type obj.

Without the cast, the function has the return type (obj -> obj list), a completely different function signature from (obj -> obj) that funny requires.


This style of programming can be useful in a narrow, specific set of problems (like instantiating a collection of objects with an unknown interface, passing a bunch of parameters to a function that simply prints out or logs each parameter, inter op with some a third-party DLL that expects an array of objects).

Outside of those narrow cases, writing code like that in a ML environment is guaranteed to get you punched in the face.

Yup. An advantage of lisp is it allows you to do that without throwing away the type safety.

; SLIME 2008-08-31
CL-USER> (defun test (number)
(let ((a '(mary had a 1 2 3)))
(+ number (first a))))
; in: LAMBDA NIL
; (+ NUMBER (FIRST A))
;
; note: deleting unreachable code
;
; caught WARNING:
; Asserted type NUMBER conflicts with derived type
; (VALUES (MEMBER MARY) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 1 WARNING condition
; printed 1 note
TEST
CL-USER> (defun test (number)
(let ((a '(mary had a 1 2 3)))
(+ number (fourth a))))
STYLE-WARNING: redefining TEST in DEFUN
TEST
CL-USER> (test 1)
2
CL-USER>


So in simple cases like you are discussing, with non-homogeneous sequences or dynamically typed variables a good compiler will still check your code for correctness, and where it can't prove correctness at compile time, it will insert runtime checks to ensure correctness.

This greatly reduces the need for ugly redundant code, and the desire to punch people in the face.

All this means however, is that you shouldn't write lisp code in ML, not that ML is a bad language to use.

Gravy
22nd September 2008, 08:15 AM
From my point of view, type errors are so frequent and easy to make that deliberately choosing to write a program in a language that can't enforce type safety before runtime is like playing sports without a cup. Its just too easy to take one in the rocks, and when it happens, it hurts. A lot...

Please share your thoughts :)Cups are commonly worn in only a few sports these days. ;)