dynamically loaded code would be lovely to have.
and go already does dynamic typing really well,
so it's a long way along the road.
tricky bits:
- types
- global state
- runtime
i'm only going to talk about the first of these in this post.
suppose one could define a dynamic module like this:
package main
func dynmain() string {
return "hello, world"
}
and you could load it like this:
x, err := runtime.Load("/path/to/dynamiclibrary")
fmt.Printf("got value from dynamic module: %s\n", x.(string))
so far so good.
but what if the module looked like this:
package main
import "os"
func dynmain() *os.File {
f, _ := os.Open("/dev/null", os.O_WRONLY, 0)
return f
}
and the loading code looked like this:
x, err := runtime.Load("/path/to/dynamiclibrary")
switch x := x.(type) {
case *os.File:
x.Write([]byte("hello, world"))
default:
panic("not a file")
}
will "hello, world" get written to /dev/null, or will the code panic?
intuitively i'd prefer the former, but i don't think it's a good
idea. the dynamically loaded module has been linked against
some version of the library code. to make the dynamic code
loading safe, you'd have to ensure that the loading code
has been linked against exactly the same version of the
code, which makes it extremely fragile.
one solution could be to agree on some set
of types that both the loading and the loaded module
will agree on - all other types test as different. call this
the set of shared types, S
A type t is a member of S if all the following conditions hold:
a) all its members are public
b) it has no concrete methods (*)
c) all component types of the members of t are members of S
(*) by "concrete method", i mean a method defined on t
with a func declaration.
Two named types are identical if they originate in the
same type declaration, or are both members of S,
otherwise identical, and declared in packages with identical
path names.
these rules means you can pass values back and forth between dynamic
modules, but no private state gets transferred, unless it's
inside an interface (and therefore safely encapsulated).
the compiler could check that the type returned by dynmain()
is actually a shared type.
the upshot of this proposal is that you'd have be careful how
you defined the interface to dynamically loaded modules,
but that's probably a good thing.
most current interfaces are allowed through under these
rules (e.g. io.Reader, image.Image), but some expectations
won't be fulfilled (e.g. image.AlphaColor is not a shared type)
perhaps this is too restrictive? thoughts?