EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

Embedded Scripting -- Tcl? Lua? Thoughts? Suggestions?

Started by Tim Wescott March 13, 2015
On 3/15/2015 6:44 PM, George Neuner wrote:
> On Sat, 14 Mar 2015 22:30:30 -0700, Don Y <this@is.not.me.com> wrote: > >> On 3/14/2015 8:59 PM, George Neuner wrote: >> >>> But a tuple semantically is different from multiple values. A tuple >>> is a single container with multiple content - the container must be >>> "opened" to get at its content whereas the individual values >>> (typically) do not. >> >> That's not required in limbo. E.g., it is common for a function to >> return a tuple: one member being a flag (success/fail) with the other(s) >> being the expected result(s). > > And how do you access those values in the tuple - surely it can't be > the same as values not in the tuple.
As in the example I posted. You *think* of tuples as structs. But, they don't really exist in an abstract sense. I.e., they only exist when you are accessing them -- as (...). E.g., you can have a function return a (string, int). Another similar function can also return a (string, int). You don't consider the results of these two functions to be "the same type" -- in the sense that (in C) you would create a typedef struct that *both* would use. You don't reference the "members" of a tuple by "names/identifiers". Rather, *positionally*, via a "(...) = <tuple_type>" assignment. One of the annoyances of Limbo is exactly that. You end up with code stanzas like: (name, count) := function_that_returns_a_string_and_an_int(...); ... (name, count) := function_that_returns_a_string_and_an_int(...); The second invocation screws you because you've already got a "name" and "count" in the current scope. So, you end up rewriting: (name, count) := function_that_returns_a_string_and_an_int(...); ... (name, count) = function_that_returns_a_string_and_an_int(...); or, declaring name and count "up top" somewhere and then: (name, count) = function_that_returns_a_string_and_an_int(...); ... (name, count) = function_that_returns_a_string_and_an_int(...);
> Multiple-value return is more complex than single-value return. Many > languages allow functions to return a container type to preserve the > (relative) simplicity of the single-value return. Ansi C does this > with structs.
Yes. The struct analogy in Limbo is the adt (Abstract Data Type) which is often a (user-)named type. Modules (which are a formal type) are essentially adt's -- the data and function members thereof accessed through pointers to the underlying module: "module->member" notation (where "module" is a pointer to a loaded "Module")
> I don't know what Limbo does, but tuple using languages typically > allocate the tuple and its contents separately and then return a > reference to the tuple. Which goes back to what I said about the cost > of accessing the values.
Limbo avoids this by letting (forcing!) you to declare the variables into which the result(s) get stuffed. So, it's just syntactic sugar, of a sort. I.e., in the first (":=") example, name and count are declared (created) in the current stack frame so there's a place for these "return values" to get stuffed. In the second ("=") example, the variables were previously declared (and accessible in this scope) so the same idea applies. There's no "hidden" allocation/GC required (beyond that of cleaning up the stack frame when the block exists).
Paul Rubin <no.email@nospam.invalid> wrote:

> 6. You can also consider Python though its embedding challenges are > harder. I think it can be used without Linux but I've never done it.
I don't know anything about it, but Micro Python is a thing that exists. It is a Python 3 implementation optimized for microcontrollers. <http://micropython.org/> -a
Anders.Montonen@kapsi.spam.stop.fi.invalid writes:
> I don't know anything about it, but Micro Python is a thing that exists. > It is a Python 3 implementation optimized for microcontrollers. > <http://micropython.org/>
It's too large for this purpose, apparently. I'm told it needs around 256k. I've been looking at Microscheme and Picobit (both small Scheme implementations). Microscheme (intended for AVR8) is too minimalistic but Picobit is interesting. There is an ARM port of Picobit but I don't know how well maintained it is.
On 3/14/2015 8:59 PM, George Neuner wrote:
> On Sat, 14 Mar 2015 13:14:12 +1100, Clifford Heath > <no.spam@please.net> wrote: > >> The only real use for multiple assignment is swapping values without >> using an explicit temporary: >> x, y = y, x > > No, there are other uses - the most common to return both a result and > error/status from a function. > > However, multiple assignment - or equivalently, multiple value return > - too often is abused by allowing some of the values to be elided. > There are times when all values aren't interesting, but IMO there > should be a syntactic placeholder that is not assigned to.
Limbo supports this with the "nil" object. It;s required for parameters in tuples that are to be "ignored". It's also "good practice" to use it in places where you *want* to ignore something (e.g., a return value) and yet indicate that you are deliberately ignoring it (vs. "you forgot about it"). E.g., nil = malloc(...)
On Mon, 16 Mar 2015 15:42:18 -0700, Don Y <this@is.not.me.com> wrote:

>On 3/14/2015 8:59 PM, George Neuner wrote: > >> ... multiple assignment - or equivalently, multiple value return >> - too often is abused by allowing some of the values to be elided. >> There are times when all values aren't interesting, but IMO there >> should be a syntactic placeholder that is not assigned to. > >Limbo supports this with the "nil" object. It;s required for parameters >in tuples that are to be "ignored". > >It's also "good practice" to use it in places where you *want* to ignore >something (e.g., a return value) and yet indicate that you are deliberately >ignoring it (vs. "you forgot about it"). > >E.g. nil = malloc(...)
Yes. Deliberately ignoring returns is fine. My gripe is against allowing to ignore the returned value(s) completely ... which too many languages allow ... or to catch the first value (or first N values) of a multiple return and ignore the rest *without* any syntactic cues. Lisp has both flaws - Scheme has only the first. In either language you can completely ignore all the returned values, but if you want the 3rd value of 5, in Lisp you need to catch at least the first 3 values; in Scheme you must catch all 5 values. Unfortunately neither Lisp nor Scheme allows reusing a name to bind multiple values from the same RHS expression, nor do they have explicit syntax to suppress binding of particular values. [Lisp's multiple-value-bind does implicit suppression of trailing values, however there is no warning if you don't catch everything. Scheme's let-values complains if the LHS doesn't match the RHS.] I like certain aspects of multiple return/assignment and I included it in one of the languages I designed. Like Scheme, I required that every LHS identifier be matched by a RHS value, but I also included a LHS placeholder which throws away the corresponding RHS value: I used "_" because that what Prolog used for anonymous matching and I like the visual effect of a "hole" in the expression. YMMV. I also let the RHS be a list of arbitrary expressions so long as it produces the correct number of values. So you could say stuff like: a,_,c,d,e = func_returning_3(), func_returning_2(); and a,b,_,d,e = 1,2,func_returning_2(),(1+2); It was not any more efficient than evaluating the expressions and performing the assignments in sequence, but I considered that choosing to use the multiple assignment form might be conveying something semantically important to the programmer ... and it was fairly easy to do once I had multiple return. George
On 3/17/2015 1:25 AM, George Neuner wrote:
> On Mon, 16 Mar 2015 15:42:18 -0700, Don Y <this@is.not.me.com> wrote: > >> On 3/14/2015 8:59 PM, George Neuner wrote: >> >>> ... multiple assignment - or equivalently, multiple value return >>> - too often is abused by allowing some of the values to be elided. >>> There are times when all values aren't interesting, but IMO there >>> should be a syntactic placeholder that is not assigned to. >> >> Limbo supports this with the "nil" object. It;s required for parameters >> in tuples that are to be "ignored". >> >> It's also "good practice" to use it in places where you *want* to ignore >> something (e.g., a return value) and yet indicate that you are deliberately >> ignoring it (vs. "you forgot about it"). >> >> E.g. nil = malloc(...) > > Yes. Deliberately ignoring returns is fine. > > My gripe is against allowing to ignore the returned value(s) > completely ... which too many languages allow ... or to catch the > first value (or first N values) of a multiple return and ignore the > rest *without* any syntactic cues.
Exactly. This is the same sort of thing as adding an explicit cast: "Yeah, I *know* this is an APPLE. But, it's safe for us to PRETEND it's an ORANGE, right now..." Show the *next* developer that you were aware of something -- and took steps to deliberately ignore it -- instead of potentially leaving him wondering, "Why didn't he check the error code, here?"
> Lisp has both flaws - Scheme has only the first. In either language
The nature of lists -- they have no prescribed lengths! :-/
> you can completely ignore all the returned values, but if you want the > 3rd value of 5, in Lisp you need to catch at least the first 3 values; > in Scheme you must catch all 5 values. Unfortunately neither Lisp nor > Scheme allows reusing a name to bind multiple values from the same RHS > expression, nor do they have explicit syntax to suppress binding of > particular values. > [Lisp's multiple-value-bind does implicit suppression of trailing > values, however there is no warning if you don't catch everything.
------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ That's the nature of this subthread. Like being able to mark the source to say "ignore this warning in this place. And, if the warning DOESN'T appear, bring that to my attention!!"
> Scheme's let-values complains if the LHS doesn't match the RHS.] > > I like certain aspects of multiple return/assignment and I included it > in one of the languages I designed. Like Scheme, I required that > every LHS identifier be matched by a RHS value, but I also included a > LHS placeholder which throws away the corresponding RHS value: I used > "_" because that what Prolog used for anonymous matching and I like > the visual effect of a "hole" in the expression. YMMV.
I'm not fond of '_' in any use. Even seeing it in identifiers makes me cringe. It still remains part of my typedef names (e.g., foo_t) but I've been disciplining myself to get rid of it there, as well. Limbo's "nil" (for placeholder) seems to stand out well enough (i.e., it doesn't bring up suggestions of some other identifier you might have encountered in your code). It also has value (actually, there's a pun hidden in that!) in assignments: comm_links: list of channel; ... comm_links = nil; // cut all lines!
> I also let the RHS be a list of arbitrary expressions so long as it > produces the correct number of values. So you could say stuff like: > > a,_,c,d,e = func_returning_3(), func_returning_2(); > and > a,b,_,d,e = 1,2,func_returning_2(),(1+2);
A list is a list, right? :>
> It was not any more efficient than evaluating the expressions and > performing the assignments in sequence, but I considered that choosing > to use the multiple assignment form might be conveying something > semantically important to the programmer ... and it was fairly easy to > do once I had multiple return.
I'd be leary of a missing terminator causing two func() invocations (on successive lines) to be cat'd (cons'd) together to fill a single list. But, having/requiring number of values to equal number of variables backstops that. (Too early in the morning to wonder if there are any pathological cases where you could still get screwed by something like that...) C off to new class so I may actually get time for *work*, today! (what a novel concept!) OTOH, I'm still stumped on some syntax for the IDL so maybe work isn't such a good idea, after all! :-/
On Tue, 17 Mar 2015 10:39:25 -0700, Don Y <this@is.not.me.com> wrote:

>On 3/17/2015 1:25 AM, George Neuner wrote: > >> My gripe is against allowing to ignore the returned value(s) >> completely ... which too many languages allow ... or to catch the >> first value (or first N values) of a multiple return and ignore the >> rest *without* any syntactic cues. > >> Lisp has both flaws - Scheme has only the first. ... > >The nature of lists -- they have no prescribed lengths! :-/
Not lists - in both cases it's multiple value binding. If a function returns multiple values, Lisp by default catches only the first value ... if you want to catch others you have to use multiple-value-bind. multiple-value-bind is a kitchen sink that treats the returned values as a sequence - it can catch just the N head values of the sequence and it can supply defaults if the sequence isn't long enough. In Scheme you must either catch or ignore *all* of the return values - if you don't Scheme complains. let-values also [theoretically] treats the returned values as a sequence, but the sequence must exactly match the binding list, so there are no defaults and no skipped returns. multiple-value-bind [or even a more general solution] can be written in Scheme using call-with-values and catching the returns into a list or vector before further processing, but the flexibility of Lisp's solution isn't there by default. Which mostly is a Good Thing(tm)[*] in my opinion.
>I'm not fond of '_' in any use. Even seeing it in identifiers makes >me cringe. It still remains part of my typedef names (e.g., foo_t) >but I've been disciplining myself to get rid of it there, as well. > >Limbo's "nil" (for placeholder) seems to stand out well enough (i.e., >it doesn't bring up suggestions of some other identifier you might >have encountered in your code).
I don't like punning LHS placeholders and RHS expressions. What does it mean to write "nil = nil" ? IMO, the placeholder should be a reserved identifier that doesn't have any other meaning - including on the RHS where it also might be used, e.g., in an argument list to indicate a default value should be substituted. That doesn't leave a lot of choices on an ASCII keyboard unless you're willing to give it some kind of proper name. I'm not per se opposed to the placeholder being a multi-character symbol. Something like [] or %% might work fine, but I think it should not be easily confused with other things that might be in an expression list. YMMV.
>It also has value (actually, there's a pun hidden in that!) in >assignments:
I'm not a big fan of punning. Why I prefer Scheme to Lisp.
>> a,_,c,d,e = func_returning_3(), func_returning_2(); > >I'd be leary of a missing terminator causing two func() invocations >(on successive lines) to be cat'd (cons'd) together to fill a single >list. But, having/requiring number of values to equal number of >variables backstops that. (Too early in the morning to wonder if >there are any pathological cases where you could still get screwed >by something like that...)
Leaving off the terminator would cause an error because the list requires a separator. However, if the programmer miscounts bindings vs values, mistakenly ends the RHS with a separator instead of a terminator *and* forgets to catch a value produced by the following extraneous expression: e.g., a,b,c,d,e = funcA_returns_2(), funcB_returns_2(), <- mistaken comma func_returns_1(); well, there's no way to tell that that isn't exactly what was meant. I'm absolutely dead-set against significant whitespace ala Python's indentation which creates more problems than it solves. Cripes! even Fortran finally stopped requiring it. The above example wouldn't work if the extraneous expression produces no value or produces more than 1 value. The extraneous expression (or expressions as it might be with more separator mistakes) have to exactly fill the binding list. So, although possible, it takes a confluence of 2 mistakes and unlucky happenstance to occur. You can't guard against everything - there would be virtually no software if you did because 99.9994% of all source input would be in error and only a tiny group of pathological masochists would ever stick with it long enough to produce anything. It would be like programming in the 1950s all over again 8-) George [*] Jerry Pournelle, 1977
On 3/17/2015 2:06 PM, George Neuner wrote:
> On Tue, 17 Mar 2015 10:39:25 -0700, Don Y <this@is.not.me.com> wrote: >> On 3/17/2015 1:25 AM, George Neuner wrote: >> >>> My gripe is against allowing to ignore the returned value(s) >>> completely ... which too many languages allow ... or to catch the >>> first value (or first N values) of a multiple return and ignore the >>> rest *without* any syntactic cues. >> >>> Lisp has both flaws - Scheme has only the first. ... >> >> The nature of lists -- they have no prescribed lengths! :-/ > > Not lists - in both cases it's multiple value binding.
Grrr... too early in the morning. :< Conflated this discussion with the (similar multivariable) issue I'm facing with my marshalling/unmarshalling code in the stubs ("Gee, it's sorely tempting to just treat this as a list of bytes and scrape them off the caller and transport them to the callee...")
> If a function returns multiple values, Lisp by default catches only > the first value ... if you want to catch others you have to use > multiple-value-bind. multiple-value-bind is a kitchen sink that > treats the returned values as a sequence - it can catch just the N > head values of the sequence and it can supply defaults if the sequence > isn't long enough. > > In Scheme you must either catch or ignore *all* of the return values - > if you don't Scheme complains. let-values also [theoretically] treats > the returned values as a sequence, but the sequence must exactly match > the binding list, so there are no defaults and no skipped returns. > > multiple-value-bind [or even a more general solution] can be written > in Scheme using call-with-values and catching the returns into a list > or vector before further processing, but the flexibility of Lisp's > solution isn't there by default. > > Which mostly is a Good Thing(tm)[*] in my opinion. > >> I'm not fond of '_' in any use. Even seeing it in identifiers makes >> me cringe. It still remains part of my typedef names (e.g., foo_t) >> but I've been disciplining myself to get rid of it there, as well. >> >> Limbo's "nil" (for placeholder) seems to stand out well enough (i.e., >> it doesn't bring up suggestions of some other identifier you might >> have encountered in your code). > > I don't like punning LHS placeholders and RHS expressions. What does > it mean to write "nil = nil" ?
I don't think it is allowed. The only syntax that might allow it would be a tuple-to-tuple assignment. "nil" isn't a proper "variable". Rather, it's an indication of "empty". E.g., for a string variable, foo: string; foo = nil; foo = ""; are indistinguishable. But, you can't (?) use '""' to indicate an empty *list*. foo: list of <whatever>; foo = <whatever> :: <whatever>; foo = nil; if (foo == nil) ... Nor, can you use '""' to indicate an unused argument in a function invocation. Or, a discarded return value in a tuple. Or, an unused "place holder" in a function declaration. E.g., the way most (C) newbies write main() would effectively be: main(nil: int, nil: list of string) instead of: main(argc: int, argv: list of string)
> IMO, the placeholder should be a reserved identifier that doesn't have > any other meaning - including on the RHS where it also might be used, > e.g., in an argument list to indicate a default value should be > substituted. That doesn't leave a lot of choices on an ASCII keyboard > unless you're willing to give it some kind of proper name.
I prefer real names for things instead of yet another cryptic "special combination" of symbols. One of limbo's problems is it's not very effective as a "read over the phone" language. (unless you preagree as to how to name particular character combinations: -> <- => :: etc)
> I'm not per se opposed to the placeholder being a multi-character > symbol. Something like [] or %% might work fine, but I think it > should not be easily confused with other things that might be in an > expression list.
> You can't guard against everything
Of course! How many times have you seen "NUL" in a piece of code and had to look at the surrounding code to understand if its a typo for "NULL"?
> - there would be virtually no > software if you did because 99.9994% of all source input would be in > error and only a tiny group of pathological masochists would ever > stick with it long enough to produce anything. > > It would be like programming in the 1950s all over again 8-)
To be fair, the tools to *write* the code are a lot more user-friendly than pulling a card from a deck and leaning on DUP until you get to the column that needs to be tweaked.
On Tue, 17 Mar 2015 19:36:34 -0700, Don Y <this@is.not.me.com> wrote:

>I prefer real names for things instead of yet another cryptic "special >combination" of symbols. One of limbo's problems is it's not very >effective as a "read over the phone" language. (unless you preagree >as to how to name particular character combinations: -> <- => :: etc)
I've never had to read - or listen to - code over the phone. 8-) I do agree that symbols can be too cryptic, but I also don't much like the Lisp way of spelling-everything-out-as-verbosely-as-possible. Lengthy names (particularly those trying to redundantly encode type) become unwieldy very quickly. There has to be some acceptable middle ground.
> ... How many times have you seen "NUL" in a piece of code >and had to look at the surrounding code to understand if its a >typo for "NULL"?
Oddly enough, I once used a Lisp variant which had both null and nil. null was nil ... I forget what nil was for. Too long ago.
>To be fair, the tools to *write* the code are a lot more user-friendly >than pulling a card from a deck and leaning on DUP until you get to >the column that needs to be tweaked.
Cards? Our machine had rows of those little "rice grain" toggle switches. We had to (re)enter the program in binary, one byte at a time, whenever the machine was reset. 8-) George
On 3/17/2015 10:50 PM, George Neuner wrote:
> On Tue, 17 Mar 2015 19:36:34 -0700, Don Y <this@is.not.me.com> wrote: > >> I prefer real names for things instead of yet another cryptic "special >> combination" of symbols. One of limbo's problems is it's not very >> effective as a "read over the phone" language. (unless you preagree >> as to how to name particular character combinations: -> <- => :: etc) > > I've never had to read - or listen to - code over the phone. 8-)
You have vision. How would a blind user "see" the code? How would I tell him what to write to solve one of his problems? [I've fed various sources into several different speech synthesizers, haptic interfaces, etc. It is only then that you truly realize how much our programming languages have come to rely on "symbols"] Or, do you just exclude them from that technology? :> One of the driving motivations for my current project is accessibility -- in more than just a "token" sense. As I said, you can adopt *conventions* to make what you see more readily conveyed in (e.g., spoken) conversation. E.g., I'd never say "colon colon equals" when reading an Algol fragment to someone...
> I do agree that symbols can be too cryptic, but I also don't much like > the Lisp way of spelling-everything-out-as-verbosely-as-possible. > Lengthy names (particularly those trying to redundantly encode type) > become unwieldy very quickly. There has to be some acceptable middle > ground.
Hence the appeal of lots of specific namespaces, short programs/modules, avoiding reuse of an identifier in a nested scope, etc.
>> ... How many times have you seen "NUL" in a piece of code >> and had to look at the surrounding code to understand if its a >> typo for "NULL"? > > Oddly enough, I once used a Lisp variant which had both null and nil. > null was nil ... I forget what nil was for. Too long ago. > >> To be fair, the tools to *write* the code are a lot more user-friendly >> than pulling a card from a deck and leaning on DUP until you get to >> the column that needs to be tweaked. > > Cards? Our machine had rows of those little "rice grain" toggle > switches. We had to (re)enter the program in binary, one byte at a > time, whenever the machine was reset. 8-)
I only saw front panels on small minis. Any of the "bigger machines" (that were shared, of necessity) were pretty much "maintained". On the odd occasion, you might have to load something off tape (DECtape) but, for the most part, the system was "up" and most jobs were just submitted on decks of cards. You did your "editing" at a keypunch terminal in another room (or, a *single* terminal set up for "one off" quicky changes (like fixing a JCL card in case of an immediate abend). It was a *delight* to advance to the point of being able to use an ASR33 w/103 modem to *type* in code! And, having a "local copy" on PPT... :-/ [I still have an ASR33] The Reading Machine had a "control panel". Pull the front (cosmetic) panel off to expose the front panel of the minicomputer. Though, the switches were sizeable -- like 1/2" wide plastic paddles. But, 16 of them ("data") grouped in octal triads. Set address. Load data. repeat. Bit-switch in your diagnostic, a loader, etc. And, count on the little ferrite cores to "remember" for you! [Our first *DRAM* machine was disturbing! "You mean it *forgets* when it powers off??"]
The 2026 Embedded Online Conference