* * * * *
Fun with static types
> The real issue is one of “type decoration”, i.e., putting in little tokens
> here and there to give hints to the compiler as to what is going on. No one
> wants to do this. The dynamic typing camp sacrifices a certain amount of
> “compile-time” error checking to avoid having to decorate the code. The
> static typing camp uses smart tools to minimize decoration, and they “grin
> and bear it” otherwise. To caraciture both camps, the dynamic type camp
> says “it is so painful to type ‘integer’ that I never ever am willing to do
> it, but I'll live with runtime exceptions” whereas the static-typing camp
> says “my type system is so smart I almost never have to add declarations,
> but the thought of the computer ‘guessing’ my intent and ‘dwimming (verb
> form: Do What I Mean)’ my code is anathema. So I'd rather type the
> occasional type declaration to avoid it.”
>
> As I mentioned before, I'm very much in the “dynamic” camp. When I have to
> work with a language which is not only statically typed, but provides no
> tools for reducing the amount of type decoration, and furthermore still
> allows one to write meaningless expressions that appear to be meaningful, I
> end up feeling encumbered. So I don't think of “lightweight” and “type
> declarations” go together very well. I'm sure some agree and others
> disagree.
>
“Re: cheerful static typing (was: Any and Every … (was: Eval)) [1]”
I quote this because this seems to be indicative of the current “anti- static
type” camp of programmers that seems to be very popular these days, which
seems to come down to, “I hate typing at the keyboard.” That, and “I hate
thinking about my program [2]” (and here you clearly see what side of the
debate I'm on—I'm a static-type fascist). But over the past few days I came
up with an example (and I wouldn't be surprised if someone else hasn't
thought of this) where static typing can actually increase the expressiveness
(read: less typing required) of a language.
I'll start with an example in C (I would use Ada, but it's been years since I
last used it, I don't have my references handy, and it would be about twice
as verbose, so there's some merit to the “too many keys” argument of the
dynamicists):
-----[ C ]-----
{
FILE *in;
FILE *out;
int c;
in = fopen("input","r");
out = fopen("output","w");
/*-----------------------------
; yes, there's a bug here, but
; that's due to a bug in the
; design of the C Standard Library
; API. feof() only returns TRUE
; *after* we've attempted to
; read past the last character.
;-----------------------------*/
while(!feof(in))
{
c = fgetc(in);
c = toupper(c);
fputc(c,out);
}
fclose(out);
fclose(in);
}
-----[ END OF LINE ]-----
It's not a terrible amount of code (as say, compared to the equivalent in
this age's Cobol—Java) but since we already have the types (that the fascist
language C requires) why not do something other than simple type checking?
Why not put that knowledge to use and have the compiler write some code for
us? We could instruct the compiler (for our now hypothetical language) that
if it sees an assignment of the form:
character-type “=” file-type
it should read the next byte from the file. Conversely, if it sees
file-type “=” character-type
it should then write the given character to the file.
> {
> FILE in;
> FILE out;
> char c;
>
> in = fopen("input","r");
> out = fopen("output","w");
>
> while(!feof(in))
> {
> c = in;
> out = toupper(c);
> }
>
> fclose(out);
> fclose(in);
> }
>
It may look unusual, but a similar notion exists in another language right
now, one that is (or rather, was) quite popular: Perl.
-----[ Perl ]-----
{
open(IN,"input");
open(OUT,">output");
while($line = <IN>)
{
print OUT $line;
}
close(OUT);
close(IN);
}
-----[ END OF LINE ]-----
(Of course, since Perl is dynamic, you have to have the file handle between
the angle brackets, which tells Perl you want to do a read from the file.
Attempting to do:
-----[ Perl ]-----
#!/usr/bin/perl
while($line = <STDIN>)
{
<STDOUT> = $line;
}
-----[ END OF LINE ]-----
fails with
-----[ data ]-----
Can't modify <HANDLE> in scalar assignment at ./t.pl line 5, near "$line;"
Execution of ./t.pl aborted due to compilation errors.
-----[ END OF LINE ]-----
so it's almost what we're doing here, but just in one special case)
Now, while we're at it, why not add a bit more syntactic sugar and use a
common object oriented notation (and at the same time, if a [DELETED-function
DELETED] method takes no formal parameters, just dump the superfluous
paranthesis):
-----[ pseudo ]-----
{
File in;
File out;
char c;
in = File.read("input");
out = File.write("output");
while(!in.eof)
{
c = in;
out = c.toupper;
}
out.close;
in.close;
}
-----[ END OF LINE ]-----
Wait a second … let's go even futher. Why bother with the actual function
names read and write? By making the file types more specific, and with a
smart enough compiler to call destructors upon leaving the functional scope,
we can indeed cut out even more clutter:
-----[ C ]-----
{
FileIn in = "/tmp/input";
FileOut out = "/tmp/output";
char c;
while(in)
{
c = in;
out = c.toupper;
}
}
-----[ END OF LINE ]-----
So, we've instructed the compiler to note
file-input-type “=” string
and therefore open the requested file for input; the same can be said for
file-output-types as well. In a boolean context, it makes sense to check if
the file is at the end.
Now, in all this, I've been transforming the data, but if I want to skip even
that, why should I have to write:
-----[ pseudo ]-----
{
FileIn in = "/tmp/input";
FileOut out = "/tmp/output";
char c;
while(in)
{
c = in;
out = c;
}
}
-----[ END OF LINE ]-----
When I could just:
-----[ pseudo ]-----
{
FileIn in = "/tmp/input";
FileOut out = "/tmp/output";
out = in;
}
-----[ END OF LINE ]-----
But is that expecting too much? In the previous example, we get an actual
file copy, but what if we don't want that? What if we want to copy not the
file, but the “variable” in itself? Well, here, types again come to the
rescue because copying the contents of an “input file” to the contents of
another “input file” doesn't make semantic sense, so in that case, it's the
variables themselves that are copied, not the data in the files they
represent:
-----[ pseudo ]-----
{
FileIn in1 = "/tmp/input";
FileIn in2;
in2 = in1; // now in2 and in1 reference
... // the same input file
}
-----[ END OF LINE ]-----
The argument could be made that just like polymorphism and operator
overloading, this will lead to inscrutable code, but personally, I'd love a
language that would allow me to do such things (and I'm not even sure what to
call this … it's not exactly a type conversion). I've also glossed over how
to code these conversions but I'm sure that just like operator overloading in
C++ a syntax can be evolved.
[1]
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01231.html
[2]
gopher://gopher.conman.org/0Phlog:2006/06/05.1
Email author at
[email protected]