It is perfectly possible in a statically typed language. The whole java.lang.reflect
thingy is about doing that. Of course, using reflection gives you as much type safety as you have with Lisp. On the other hand, while I do not know if there are statically typed languages supporting such feature, it seems to me it could be done.
Let me show how I figure Scala could be extended to support it. First, let's see a simpler example:
def apply[T, R](f: (T*) => R)(args: T*) = f(args: _*)
This is real Scala code, and it works, but it won't work for any function which receives arbitrary types. For one thing, the notation T*
will return a Seq[T]
, which is a homegenously-typed sequence. However, there are heterogeneously-typed sequences, such as the HList.
So, first, let's try to use HList
here:
def apply[T <: HList, R](f: (T) => R)(args: T) = f(args)
That's still working Scala, but we put a big restriction on f
by saying it must receive an HList
, instead of an arbitrary number of parameters. Let's say we use @
to make the conversion from heterogeneous parameters to HList
, the same way *
converts from homogeneous parameters to Seq
:
def apply[T, R](f: (T@) => R)(args: T@) = f(args: _@)
We aren't talking about real-life Scala anymore, but an hypothetical improvement to it. This looks reasonably to me, except that T
is supposed to be one type by the type parameter notation. We could, perhaps, just extend it the same way:
def apply[T@, R](f: (T@) => R)(args: T@) = f(args: _@)
To me, it looks like that could work, though that may be naivety on my part.
Let's consider an alternate solution, one depending on unification of parameter lists and tuples. Let's say Scala had finally unified parameter list and tuples, and that all tuples were subclass to an abstract class Tuple
. Then we could write this:
def apply[T <: Tuple, R](f: (T) => R)(args: T) = f(args)
There. Making an abstract class Tuple
would be trivial, and the tuple/parameter list unification is not a far-fetched idea.
Invoke
that is likeapply
. – Mizeapply
is such a program. – RieblingInvoke
function has been around since 1.0 so it uses reflection. Dynamically generating glue code is a 4.0 feature. – Mizeapply
andeval
(especially a metarciculareval
) are some of the programs that are often claimed by proponents of dynamic typing to be impossible to implement in statically typed languages. At least in practically existing ones. For example, if you look at the source code for Haskell'sdynApply
in theData.Dynamic
module, which is probably the closest thing to Scheme'sapply
, you will find that it uses theunsafeCoerce
function, which is basically the same as an unrestricted unsafe cast in C, and thus explicitly and deliberately circumvents the type system. – Rieblingeval
without circumventing the type system somehow. In fact, if you plug all the holes and take static typing to its logical conclusion, it's provably impossible to write an interpreter for a language in itself at all. Having used Scheme and Ruby before moving to Haskell, I do miss this sort of thing sometimes... – Quaverapply
, to a degree, at the condition that arguments types and arity are fixed at compile-time. – Cointon