Hacker News new | past | comments | ask | show | jobs | submit login

I've long wished to have the free time to write a Tcl-derived language, because it really is so elegant in many ways, it just needs a bit of modernization in some areas. It's been years since I really thought much about this but I recall one of the things it's missing is closures (it does have lambdas at least).

Reading through this article, the memoize implementation does have an issue which is if the memoized command wants to call uplevel or upvar it'll get the wrong stack frame. If I were writing this I'd structure it so it's used like

  proc myMemoizingProcedure { ... } {
    memoize {
      ... the rest of the code ...
    }
  }
such that it can just `uplevel` the code. Or better yet I'd make `memoize` replace the `proc` keyword (or perhaps `memoize proc myMemoizingProcedure …`).

EDIT: I suppose memoizing makes no sense in a procedure that wants to use upvar or uplevel though, because memoizing only works for pure functions.




There are these pages on the wiki regarding closures, and a reference to needing to change the internals of tcl_ObjType in order to implement them.

https://wiki.tcl-lang.org/page/Closures

https://wiki.tcl-lang.org/page/Emulating+closures+in+Tcl

TCL 9 has surely fiddled with tcl_ObjType I hope, but it doesn't seem like it from a glance.


Closures are not easy to fit with "everything is a string", which favors dynamic scoping over lexical scope. I wonder what you'd change to make tcl more amenable to modernization.


Tcl totally has lexical scoping, through proc and apply that create a new frame and thus new local bindings. Closures with explicit bindings (as opposed to tree-walking to find free variables to match with [info locals] which is something Tcl really can't do due to its "list == atom" thing) are easy to do through apply and a way to store the environment.

The last implementation in https://wiki.tcl-lang.org/page/Closures is pretty nice.


That last implementation is interesting, although it requires using the `applyc` command to invoke the closure because it has to take it by name instead of by value, which is an unfortunate limitation. The thing you can't do is make a closure that can be invoked just with `apply`. Offhand the only way I can think of to try that is to store the closure data globally under a unique name, but then there's no way to clean up that data when the closure goes away since Tcl doesn't have finalizers (you can run code when a variable goes out of scope, but you can't do it for the value itself).


Yeah, I've been thinking about it for a bit, and the "problem" is that the captured environment needs to be stored somewhere in order to be modified.

applyc could be bundled in the closure to use via `{*}$closure args...` but the fact that it needs to be named defeats the concept of anonymous functions, eh.


I've been toying with building a Tcl-inspired language that basically does away with "{ ... } is just a fancy way to make a string" and treats blocks similarly to how a Lisp treats s-exps - so you could write things like:

    type Point struct {
        X int
        Y lnt
    }
Where "struct" maybe could just be a basic macro, but maybe it could also be smart enough to tell the compiler that "lnt" around "Y lnt" is not a type and suggest that maybe you meant "int".

I think a lot of this ground was treaded with modern JS compilers (that had to infer a lot of this kind of information from very little context), or efforts like Python type annotations. Dynamic languages are still cool, and even cooler when the compiler can still somehow actively help you.


jimtcl, an alternative, simpler, much smaller implementation of tcl DOES have proper closures, along with other nicities like unifying arrays & dicts.


jimtcl was originally created by the same Antirez who wrote the linked article.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: