Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

    1 + eval(read_from_the_network())
> If we get an integer, that expression is fine; if we get a string, it's not. We can't know what we'll get until we actually run, so we can't statically analyze the type.

> The unsatisfying solution used in practice is to give eval() the type Any, which is like Object in some OO languages or interface {} in Go: it's the type that can have any value. Values of type Any aren't constrained in any way, so this effectively removes the type system's ability to help us with code involving eval. Languages with both eval and a type system have to abandon type safety whenever eval is used.

No, you can give EVAL a returning type of any (T) but still make type inference work.

    (+ 1 (eval (read)))
The meaning of a type in SBCL is: if an expression evaluates without error, then its type is .... In other words, the type of the above expression is NUMBER.

There is also a NIL type (bottom) which represent the empty set of values. If a function has a return type of NIL, it means that it does not return a value normally. This is the case for the ERROR function, or the (LOOP) expression.



> if an expression evaluates without error, then its type is…

What’s curious is that this is true in every language with general recursion/looping:

    int f(int n) {
        while (true) {}
        return n + 1;
    }
(In other words, the type of “f” isn’t the logical formula “a → a”, but rather “a → ¬¬a”.)

The type of “eval” can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on to use.

In Haskell, with a data type:

    data Value = Integer Int | Text String | ...

    eval :: String -> Value

    foo = do
      value <- read_from_the_network
      let result = eval value
      case result of
        Integer n -> return (1 + n)
        _ -> error "that was not an integer!"
Or with RTTI:

    eval :: String -> Dynamic

    foo = do
      value <- read_from_the_network
      let result = eval value
      case fromDynamic result of
        Just n -> return (1 + n)
        Nothing -> error "that was not an integer!"
“fromDynamic” has the type:

    Typeable a => Dynamic -> Maybe a
Which is to say “given a dynamic value, and its runtime type information, I can either give you the value back with a static type, or nothing if I can’t perform the downcast”—much like “dynamic_cast” on pointer types in C++.


> What’s curious is that this is true in every language with general recursion/looping:

Yes, thanks for pointing that out.

> The type of “eval” can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on to use.

I'd say that's what type T stands for in Lisp, since you can then dynamically dispatch on the actual type of the value.


> The type of “eval” can also be fully static; it can just return a type that you have to do case analysis or runtime type inspection on to use.

Yes! The problem is, we don’t have a static type system which would allow us to intensionally analyse syntax at runtime. See section 9 of Sheard (2001) “Accomplishments and research challenges in meta-programming”.

https://www.dropbox.com/s/pathrmr7rufkw1d/Sheard2001.pdf

If you’re interested in this subject, perhaps drop in to #dependent on Freenode IRC, or ping me on Twitter.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: