I love that the whole C side of embedding Tcl fits on a postcard. Tcl_CreateInterp, evaluate a script with Tcl_EvalEx or Tcl_EvalObjEx, read the answer with Tcl_GetObjResult. Everything that crosses is a string, and a string is the one type both sides already agree on!
Well, almost. A Tcl_Obj carries a string representation and, once it has been used as something else, an internal one too. Use the same value as a list in one place and as a number in the next, and each use throws away the other internal representation and rebuilds its own from the string. That is shimmering, and it stays invisible until it shows up in a profile.
The other thing that doesn't cross is the thread. An interpreter belongs to the thread that created it, so the host can't just call into it from a worker pool. (A host program forgets this remarkably fast.)
So what crosses besides the function name? Text, a cache of what that text meant last time, and a thread identity. Two of the three are easy to miss.