A type command...

Started by cormullion, November 09, 2007, 11:47:49 AM

Previous topic - Next topic

cormullion

Perhaps it's my strange way of writing code, but I seem to occasionally have need of a function that tells me what type of 'thing' I'm handling. Say after a lookup or something, you don't quite know what you've got...



I suppose it's easy to write a 'predicate-gamut' function that tries every available predicate to see what it is, but I seem to remember that other languages have a 'type-of' command? (Or have I got that wrong?)



It seems like a nice introspective tool to have around. Probably not too hard to construct as a macro...

Jeff

#1
As far as I know, there are only macros, lambdas, strings, integers, floats, symbols, contexts and lists in newLISP.  Since there aren't arbitrary, user-defined types, a simple macro should do it.


(define-macro (type)
  (cond
    ((integer? (args 0)) "integer")
    ((float? (args 0)) "float")
    ((symbol? (args 0)) "symbol")
    ((string? (args 0)) "string")
    ((lambda? (args 0)) "lambda")
    ((macro? (args 0)) "macro")
    ((context? (args 0)) "context")
    ((list? (args 0)) "list")
    ((nil? (args 0)) "nil")
    (t nil)))


Careful with accepting too many data types in one function, though.  Especially in a dynamically scoped language, it's a good idea to be very sure of what is coming in and what is going out.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

cormullion

#2
Thanks, Jeff. You may be right - I should perhaps rethink the approach.



The problem areas are those like this:


(replace {x} t (lookup s table) 0)

which is perhaps too condensed: without testing what lookup returns, it's not certain whether replace will work or fail.

m35

#3
You may be interested in this ancient thread :)

http://www.alh.net/newlisp/phpbb/viewtopic.php?p=219">http://www.alh.net/newlisp/phpbb/viewtopic.php?p=219



Note that the values have changed since 2003 so you'll need to take at look at the newLISP source code for the current values.

Cyril

#4
On http://newlisp-on-noodles.org/wiki/index.php/Predicates">this page are shown both test-all-predicates and dump-based solutions.
With newLISP you can grow your lists from the right side!

cormullion

#5
Aah - thanks guys. I hadn't thought to search, not being confident enough that anybody else would have wanted something similar.

m i c h a e l

#6
Quote from: "Cormullion"Perhaps it's my strange way of writing code, but I seem to occasionally have need of a function that tells me what type of 'thing' I'm handling.


This is exactly what polymorphism is all about. Send the message to the object, and let the object decide how to do it. Here's an important thing to remember when doing objects: Tell, don't ask. Instead of retrieving the data and acting upon it, just tell the object to do it. It already has the data! Lutz's new : function makes this type of programming easy now. Granted, objects are probably overkill for most simple scripts, but even here in your script, polymorphism is popping up.



Even the built-ins can be wrapped within objects and used polymorphically:


> (set 'i1 '(INT 42))
(INT 42)
> (set 'f1 '(FLOAT 42))
(FLOAT 42)
> (define (INT:INT (n 0)) (list INT n))
(lambda ((n 0)) (list INT n))
> (define (INT:+) (INT (apply + (map (curry nth 1) (args)))))
(lambda () (list INT (apply + (map (curry nth 1) (args)))))
> (define (FLOAT:FLOAT (n 0)) (list FLOAT n))
(lambda ((n 0)) (list FLOAT n))
> (define (FLOAT:+) (FLOAT (apply add (map (curry nth 1) (args)))))
(lambda () (list FLOAT (apply add (map (curry nth 1) (args)))))
> (:+ f1 '(FLOAT 54) (FLOAT 33.3))
(FLOAT 129.3)
> (:+ '(INT 54) (INT 33) i1)
(INT 129)
>


m i c h a e l

Jeff

#7
Yes, but polymorphism comes with its own laundry list of problems.  A function ends up becoming a huge switch statement.  Changing an object then requires changing the function.



Ruby attempts to solve this by having every class define it's own operators so that operators appear to be polymorphic, but that's just syntactic sugar.  Now, if you want to change the way the function works, every object must be updated.  Ruby is famous for its operators and methods working inconsistently across varying objects.



The problem is that we are programming mutable states.  Classes and types and closures and hashes are all ways of trying to organize states in a manageable way.  But by separating the state from the logic, you run into the problem of which controls which.  If the state controls, then the object must define all of the ways it can be used a la ruby.  If the logic controls, then the function must determine all the types on which it can act as a functor.



Many modern infix-style functional languages (like ML and Erlang) solve this by allowing you to define a function in terms of its arguments.  Here's an example (not in any real lang):


sum int a, int b = a + b
sum float a, float b = a .+ b
sum string a, string b = concatenate a, b


The idea is that the function is defined for all states it may accept, but in one place, and the interpreter/compiler determines which to use based on the arguments.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code