Hi all,
I am new to Lisp and dynamic types. I have always programmed in
languages with static type checking Java and more recently Ocaml and
Haskell (which takes this concept to extremes). I am now checking out
Lisp, Smalltalk and Forth. I decided to learn lisp because it has the
best support.
I have looked at working Lisp programs and noticed that a lot of the
code is "if" statements to make sure that a function has received the
proper input. Most C programs seem to be written in this way too. It
seems to result in less readable code. Am I thinking about the program
in the wrong way? Should I not be thinking in terms of types at all?
I read a Best Practices book on Smalltalk and it never addressed this
issue.
Thanks for the help....
Deech
"deech" <············@gmail.com> writes:
> I have looked at working Lisp programs and noticed that a lot of the
> code is "if" statements to make sure that a function has received the
> proper input.
That isn't my experience with typical Lisp code. What working Lisp
programs are you examining? Perhaps your sample isn't very
representative.
Zach
On Feb 9, 2:40 pm, Zach Beane <····@xach.com> wrote:
> "deech" <············@gmail.com> writes:
> > I have looked at working Lisp programs and noticed that a lot of the
> > code is "if" statements to make sure that a function has received the
> > proper input.
> That isn't my experience with typical Lisp code. What working Lisp
> programs are you examining? Perhaps your sample isn't very
> representative.
ISTR one of the Lisp texts I read (Shapiro's, maybe?) tended to
emphasize use of typecases and such in order to due type checking of
arguments and raise more specific error messages. Haven't seen much of
it anywhere else, though.
Cheers, Pillsy
From: Ken Tilton
Subject: Re: I am new to Lisp and Dynamic Type Checking
Date:
Message-ID: <896zh.47$UD.26@newsfe12.lga>
deech wrote:
> Hi all,
> I am new to Lisp and dynamic types. I have always programmed in
> languages with static type checking Java and more recently Ocaml and
> Haskell (which takes this concept to extremes). I am now checking out
> Lisp, Smalltalk and Forth. I decided to learn lisp because it has the
> best support.
>
> I have looked at working Lisp programs and noticed that a lot of the
> code is "if" statements to make sure that a function has received the
> proper input.
What has "if" got to do with type-checking? And how does type-checking
tell you that input is "proper" in any way other than type?
Did you mean you found (if (typep...) do something)? That would be
dispatching on (varying functionality by) type, not error-checking, and
suggest the code was written before CLOS caught on or by a person who
does not like CLOS (and generic functions).
You better post some code next time, this makes no sense. Even if you
found someone saying (if (typep..) (do-soemthing) (error "bad arg!!!")),
hey that's their problem, few Lispniks work that way. Conversely, if you
love the validation of arg checking and miss it in CL, just write a
defun-typed macro that accepts types with parameters and expands into so
many assertions so they at least get checked at runtime.
Your whole premise is whether Lisp has to be written a certain way, and
that itself misses a huge point about Lisp: you can write any kind of
garbage in any kind of paradigm you like.
ken
--
Well, I've wrestled with reality for 35 years, Doctor, and
I'm happy to state I finally won out over it.
-- Elwood P. Dowd
In this world, you must be oh so smart or oh so pleasant.
-- Elwood's Mom
"deech" <············@gmail.com> writes:
> Hi all,
> I am new to Lisp and dynamic types. I have always programmed in
> languages with static type checking Java and more recently Ocaml and
> Haskell (which takes this concept to extremes). I am now checking out
> Lisp, Smalltalk and Forth. I decided to learn lisp because it has the
> best support.
>
> I have looked at working Lisp programs and noticed that a lot of the
> code is "if" statements to make sure that a function has received the
> proper input. Most C programs seem to be written in this way too. It
> seems to result in less readable code. Am I thinking about the program
> in the wrong way? Should I not be thinking in terms of types at all?
That doesn't sound right to me. Common Lisp has macro
specifically for this job, so you might write
> (defun add (x y)
(check-type x integer)
(check-type y rational)
(+ x y))
ADD
> (add 1 1/2)
3/2
> (add 1/2 1)
; Evaluation aborted
but the usual idea is to declare types, and make type
checking a matter of compilation policy. With CMUCL the
interpreter just ignores the type information
CL-PROMPT>>> (defun add (x y)
(declare (integer x)
(rational y))
(+ x y))
ADD
CL-PROMPT>>> (add 1/2 1)
3/2
But the default settings for the compiler take the
declarations as requests by the programmer for type checks
that signal type errors.
CL-PROMPT>>> (compile 'add)
; Compiling LAMBDA (X Y):
; Compiling Top-Level Form:
ADD
NIL
NIL
CL-PROMPT>>> (add 1/2 1)
; Evaluation aborted
The alternative is for the compiler to take the declarations
as promises by the programming which the compiler is to
trust and use to produce faster code.
There is a natural progression. Once the code works and no
longer contains type errors, one recompiles with the second
policy and obtains an improvement in performance.
Alan Crowe
Edinburgh
Scotland
Alan Crowe <····@cawtech.freeserve.co.uk> wrote:
+---------------
| ...but the usual idea is to declare types, and make type
| checking a matter of compilation policy. With CMUCL the
| interpreter just ignores the type information...
+---------------
But given that the OP is a newbie, one should point out that in
CMUCL (as with several other CLs), there is *both* a full compiler
and a (half-compiling) interpreter available to the user at all
times; interpreted code may call compiled code and vice-versa.
And while "the CMUCL interpreter just ignores the type information",
the CMUCL compiler very much does *not* ignore it, but treats it as
a promise by the programmer[1] that the types will be as declared --
which often allows *much* more efficient compiled code to be generated
but risks snot demons[2] if the programmer's promise is violated.
-Rob
[1] In ANSI Common Lisp, unlike most other languages, type declarations
are not requests by the programmer asking the compiler to check
the types, but rather are promises *by* the programmer *to* the
compiler that the the declarations will be satisfied at runtime:
http://alu.org/HyperSpec/Body/sec_3-3.html>.
3.3 Declarations
...
The consequences are undefined if a program violates a
declaration or a proclamation.
[2] One of the possible outcomes of anything in Common Lisp for
which the ANSI standard says "the consequences are undefined".
It is frequently noted that such undefined behavior could range
from "nothing" to "42" to "a core dump" to "snot demons flying
out of your nose" to "starting a thermonuclear war".[3]
[3] Any similarity to the plot of the movie "Wargames" (1983)
<http://en.wikipedia.org/wiki/WarGames> is purely coincidental.
Really. (And if you believe that, I have this bridge...)
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
Thank you all,
I really appreciate the help and I feel confident what with such a
knowledgeable community helping out, that Lisp is a great language to
work with.
Cheers,
Deech
On 2007-02-09 20:34:40 +0100, "deech" <············@gmail.com> said:
> Hi all,
> I am new to Lisp and dynamic types. I have always programmed in
> languages with static type checking Java and more recently Ocaml and
> Haskell (which takes this concept to extremes). I am now checking out
> Lisp, Smalltalk and Forth. I decided to learn lisp because it has the
> best support.
>
> I have looked at working Lisp programs and noticed that a lot of the
> code is "if" statements to make sure that a function has received the
> proper input. Most C programs seem to be written in this way too. It
> seems to result in less readable code. Am I thinking about the program
> in the wrong way? Should I not be thinking in terms of types at all?
>
> I read a Best Practices book on Smalltalk and it never addressed this
> issue.
>
> Thanks for the help....
> Deech
In Common Lisp you apply functions to objects.
The function takes arguments (zero or more).
These arguments may need to be of some types to make sense.
Example: (SIN "foo") <- you would guess that "foo" is not of the
right argument type.
? (SIN "foo")
> Error: value "foo" is not of the expected type REAL.
If you write your functions, how would you express that arguments
should be of some type in Common Lisp?
What would you may want to achieve:
* the code should be readable and self documenting
* the code should be as little code as possible
* the code should reuse standard facilities for type checking and error
handling
* the code should not cause your Lisp system to crash
* the code should not make debugging more difficult
* the code should be testable
* the code should give the user some useful feedback
* the code should not be much slower due to added checks
* the code should be changeable at runtime
Here are five different approaches:
1) Naming
(defun my-sin (a-real-number)
(sin a-real-number))
? (my-sin "foo")
> Error: value "foo" is not of the expected type REAL.
> While executing: CCL::%SHORT-FLOAT, in process listener(1).
Problem: the error is signalled somewhere in SIN and not within your function.
Usually you would hope that your Lisp system can recover from those
errors and that you can get some meaningful backtrace to locate the
problem.
2) Homegrown runtime type checking.
(defun my-sin (a-real-number)
(if (typep a-real-number 'real)
(sin a-real-number)
(error "~a is not a real number" a-real-number)))
? (my-sin "foo")
> Error: foo is not a real number
> While executing: MY-SIN, in process listener(1).
3) Builtin standard runtime type checking using CHECK-TYPE.
(defun my-sin (a-real-number)
(check-type a-real-number real)
(sin a-real-number))
? (my-sin "foo")
> Error: value "foo" is not of the expected type REAL.
> While executing: MY-SIN, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
You also get an additional restart (Assing a new value ...) with this version.
1 > :r
(:C <n>) can be used to invoke one of the following restarts in this
break loop:
0. Return to break level 1.
1. #<RESTART ABORT-BREAK #xE384CD>
2. Assign a new value of type REAL to A-REAL-NUMBER
3. Return to toplevel.
4. #<RESTART ABORT-BREAK #xE389DD>
5. Reset this process
6. Kill this process
4) Use methods.
(defmethod my-sin ((n real))
(sin n))
? (my-sin "foo")
> Error: No applicable method for args:
> ("foo")
> to #<STANDARD-GENERIC-FUNCTION MY-SIN #x300040DA0D1F>
> While executing: #<CCL::STANDARD-KERNEL-METHOD NO-APPLICABLE-METHOD
(T)>, in process listener(1).
Above version is similar to the Smalltalk situation, where you could
send a message to an object and where the object might not understand
the message. In this case Common Lisp cannot find a method given that
you have called MY-SIN with a string.
5) Use non-standard static type checking (here using the SBCL compiler).
(defun my-sin (a-real-number)
(declare (type real a-real-number))
(sin a-real-number))
(defun foo (a)
(my-sin (concatenate 'string a "-baz")))
* (compile-file "/tmp/test.lisp")
; compiling file "/tmp/test.lisp" (written 09 FEB 2007 11:03:38 PM):
; compiling (DEFUN MY-SIN ...)
; compiling (DEFUN FOO ...)
; file: /tmp/test.lisp
; in: DEFUN FOO
; (MY-SIN (CONCATENATE 'STRING A "-baz"))
;
; note: deleting unreachable code
;
; caught WARNING:
; Asserted type REAL conflicts with derived type
; (VALUES (OR (SIMPLE-ARRAY * (*)) (AND SEQUENCE (NOT VECTOR))) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
Also: if your function would do different things based on the types of
inputs, you also have several choices:
1) use IF or COND and check the types and select the code to run
2) use TYPECASE or ETYPECASE
3) write methods for the various types with DEFMETHOD.
4) retrieve a new function based on the type of the input and invoke it.
5) Convert the input to some other type and reinvoke the function.
6) Change the class of the object to some other class and reinvoke the
function.
...