4 ms·
A fun exercise when reading posts like those is to mentally compare to TCL. It is somewhat uglier - TCL's lists are multi-valued; and newlines are signficicant,
by theamk 5y ago
A fun exercise when reading posts like those is to mentally compare to TCL. It is somewhat uglier - TCL's lists are multi-valued; and newlines are signficicant, introducing difference between "script" and "command".
Still, the basic eval command looks remarkably similar in lisp vs TCL.
- nulbyte 5y ago> TCL's lists are multi-valued Can you elaborate? I am not sure what you mean.
- adenozine 5y agoArguments are evaluated in a different order to modern lisps. It's fexprs. It's weird in tcl because you might see weird macros that are built from the inside out, which is not how they're done with lisp. Like, with syntax-case, you're replacing terms in forms, but tcl does it with upvar and uplevel, so the relation of the form (whitespace matters) and you have to manage items carefully (scoping) It sucks. Tcl will break you down. Not original commenter, btw.
- theamk 5y agoI mean that Lisp's composite type, tuple, always has only two elements. Lists are just syntactic sugar over tuples. This means it is pretty simple to iterate via recursion, without special iteration-related functions -- see _evlist in the post for example. Compared to that, TCL's composite type is "list", which has an arbitrary number of elements, and recursive iteration is not as simple. Yes, one can use "lindex"/"lrange" to do LISP-style recursive iteration, but this is not idiomatic at all and has horrible performance. Instead, people use foreach/explicit numeric indices, and this needs more support and special iterating-related functions, see [0] for example. [0] https://wiki.tcl-lang.org/page/Map+in+functional+programming https://wiki.tcl-lang.org/page/Map+in+functional+programming
- bsder 5y ago> Still, the basic eval command looks remarkably similar in lisp vs TCL. The primary difference is whether arguments are evaluated before (Scheme/Lisp) or after (Tcl) being sent to the function/command. The deferred evaluation is what allows you to do things that you need macros for in other languages. (ie. you need deferred evaluation or macros to implement short-circuit operators, for example) However, early Lisps had FEXPRs which also deferred evaluation. The late John Shutt built and entire Lisp called "Kernel" around the idea: http://web.cs.wpi.edu/~jshutt/kernel.html http://web.cs.wpi.edu/~jshutt/kernel.html I strongly recommend a read through his PhD thesis: https://www.wpi.edu/Pubs/ETD/Available/etd-090110-124904/ https://www.wpi.edu/Pubs/ETD/Available/etd-090110-124904/ Unfortunately, his implementation isn't anywhere near as good as his conception. Other people have implemented it better. http://axisofeval.blogspot.com/search/label/kernel http://axisofeval.blogspot.com/search/label/kernel http://axisofeval.blogspot.com/2011/09/kernel-underground.html http://axisofeval.blogspot.com/2011/09/kernel-underground.ht... The Oh Unix shell grabs some of the core Kernel concepts and runs with them in a useful package: https://github.com/michaelmacinnis/oh https://github.com/michaelmacinnis/oh
- theamk 5y agoIf I am reading this right, TCL's approach is neither FEXPRs nor Lisp's macros -- instead, it is a very concise "quote" command and "uplevel" command which is something I have not seen widely used in any other language. In the lisp terms, imagine a language with no macros and with immediate evaluation ("applicatives" in the Kernel-speak). And no special forms either, except for syntax-level quote command. This means that, for example, for "if" you have to quote the code parameters to make sure they are not evaluated too early: (if var '(print '(hello world)) nil) the naive implementation has an obvious problem with variable scope, which you solve with "uplevel" command which evaluates expression in caller's scope. So there really is no difference in the data vs code, syntax-wise -- the first quote in the example above defines the "code" while second one defines "data", and only the function implementation knows how each argument will be used. And that's how you build a language with homoiconicity and macro-like facilities, but without FEXPRs or applicatives or special syntax. Granted, it has plenty of downside, but it's still a very interesting approach.
- bsder 5y agoIn my opinion, there are two big things that Kernel brings to the table: 1) Arguments are not automatically evaluated before being passed to the underlying "operative" in Kernel-speak. This is important because it obviates the need for the macro machinery. This is fairly similar to Tcl. 2) Environments are first-class in Kernel. This is important because you can now constrain the scope explicitly. Do I want to evaluate this in the defining context (lexical scope)? Do I want to evaluate this in the calling context (dynamic scope)? Do I want to evaluate this is global or initial scope? All of these things are possible because environments are a thing that can be passed around. The downside is that this level of mutability seems to constrain the ability to pre-compile things. One of the "problems" that I would consider Tcl to have is that certain "atoms" don't self-evaluate. There's really no good reason for why you have to write: % if 1 {expr 2} else {expr 3} instead of % if 1 2 else 3 And no good reason why you can't write: % + 1 2 3 instead of % expr 1 + 2 3 This seems like going gratuitously out of the way to avoid "looking like Lisp" which was in its down phase by 1990.