A type command...

For the Compleat Fan
Locked
cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

A type command...

Post by 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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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.

Code: Select all

(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...

Artful code

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Post by cormullion »

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

The problem areas are those like this:

Code: Select all

(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
Posts: 171
Joined: Wed Feb 14, 2007 12:54 pm

Post by m35 »

You may be interested in this ancient thread :)
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
Posts: 183
Joined: Tue Oct 30, 2007 6:27 pm
Location: Moscow, Russia
Contact:

Post by Cyril »

On this page are shown both test-all-predicates and dump-based solutions.
With newLISP you can grow your lists from the right side!

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Post by cormullion »

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
Posts: 394
Joined: Wed Apr 26, 2006 3:37 am
Location: Oregon, USA
Contact:

Post by m i c h a e l »

Cormullion wrote: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:

Code: Select all

> (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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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):

Code: Select all

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...

Artful code

Locked