i'll reply in more detail later but for the moment i just want to clarify that i'm not za3k, although i've been collaborating with him; his priorities for the zorchpad are a bit different from my priorities for the zorzpad
you said:
> I think it's the primary use case like Java Applets and Flash preceded JS as iterations of sorta-portable and low efficiency tools which make up for it with sheer volume of high-familiarity material.
i wasn't able to parse this sentence. could you unpack it a bit?
you said:
> If you'd like, I can provide more specific suggestions.
yes, please! even if our priorities aren't exactly aligned i can surely learn a lot from you
> 'll reply in more detail later
np! I'll comment now in case it gets weird about a double-comment on the same parent by the same user.
> > I think it's the primary use case like Java Applets and Flash preceded JS as iterations of sorta-portable and low efficiency tools which make up for it with sheer volume of high-familiarity material.
> i wasn't able to parse this sentence. could you unpack it a bit?
Software that doesn't care about being well-made or efficient. It doesn't matter because it's either fun or useful.
> > If you'd like, I can provide more specific suggestions.
> yes, please! even if our priorities aren't exactly aligned i can surely learn a lot from you
This is a long topic, but some of it comes down to Decker[1] vs Octo[2]'s differences:
* Decker can be used to make and share things that process data
* The data itself can be exported and shared, including as gifs
* Octo can't really, but it does predate the AI commnity's "character cards" by shoving game data into a "cartridge" gif
* Octo has LISP Curse[3] issues
I'm serious about the LISP curse thing. In addition to awful function pointers due to ISA limitations, everyone who likes Octo tends to implement their own emulator, tooling, etc and then eventually start down the path of compilers.
I haven't gone that far yet, but I did implement a prototyping-oriented terminal library[4] for it. Since Gulrak's chiplet preprocessor[5] is so good, I didn't bother with writing my own.
> i just want to clarify that i'm not za3k
Ty for the reminder. On that note, the larger font sizes you brought up are seeming more important in this moment. I don't think I can deal with 4 x 6 fonts on tiny screens like I once could. HN's defaults are already small enough.
[1]: https://github.com/JohnEarnest/Decker https://github.com/JohnEarnest/Decker
[2]: https://github.com/JohnEarnest/Octo https://github.com/JohnEarnest/Octo
[3]: https://winestockwebdesign.com/Essays/Lisp_Curse.html https://winestockwebdesign.com/Essays/Lisp_Curse.html
[4]: https://github.com/pushfoo/octo-termlib https://github.com/pushfoo/octo-termlib
[5]: https://github.com/gulrak/chiplet https://github.com/gulrak/chiplet
with respect to software that doesn't need to be efficient, sure, there are lots of things that are usable without being efficient. but in rek and devine's notes on working offgrid, which are part of the background motivation for uxn/varvara, they say:
> Computers are generally power-sucking vampires. Choosing different
software, operating systems, or working from machines with a lower
draw (ARM) or even throttling the CPU, are some of the many things
we do to lower our power requirements. The way that software is
built has a substantial impact on the power consumption of a system,
it is shocking how cpu-intensive modern programs can be.
so uxn/varvara is not intended for software that doesn't need to be efficient, very much the contrary.
so, from my point of view, the fact that logic-intensive programs running in uxn consume 20× more energy than they need to is basically a mistake; rek and devine made choices in its design that keep it from meeting their own goals. which isn't to say it's valueless, just that it's possible to do 20× better
what i mean by 'logic-intensive' is programs that spend most of their time running uxn code doing some kind of user-defined computation, like compilation, npc pathfinding, or numerical integration. if your program spends most of its cpu blitting sprites onto the screen, well, that's taken care of by the varvara code, and there's nothing in the varvara definition that requires that to be inefficient. but uxn itself is very hard to implement efficiently, which creates pressure to push more complexity into varvara, and varvara has ended up fairly complex and therefore more difficult than necessary to implement at all. and i think that's another failure of uxn/varvara to fulfill its own ideals. or anyway it does much worse according to its own ideals than a better design would
how much complexity does varvara impose on you? in https://news.ycombinator.com/item?id=32219386 https://news.ycombinator.com/item?id=32219386 i said the uxn/varvara implementation for the nintendo ds is 5200 lines of c, so roughly speaking it's about 20× more complexity than uxn itself
and that's what i mean by 'and it's not that simple'. in the comment i linked above, i pointed out that chifir (the first archival virtual machine good enough to criticize, which is a somewhat different purpose) took me 75 lines of c to implement, and adding graphical output to it with yeso required another 30 lines of code. you might reasonably wonder how much complexity i'm sweeping under the carpet of yeso, since surely some of those 5200 lines of code in the uxn/varvara implementation for the nintendo ds are imposed by the ds platform and not by varvara. the parts of yeso used by my chifir implementation compiled for x-windows are yeso.h, yeso-xlib.c, and yeso-pic.c, which total 518 lines of code according to either sloccount or cloc.
still, uxn, even with varvara, is much better than anything else out there; like i said, it's the first effort in this direction that's good enough to criticize
i don't understand what you mean about octo vs. decker but possibly that's because i haven't tried to use either one
you definitely can't deal with a 4×6 font on the ls027b7dh01 display i'm using. it's 35mm tall, including the pixel-less borders, and 240 pixels tall. so 6 pixels is 0.88 mm, or a 2.5 point font. even young people and nearsighted people typically need a magnifier to read a 2.5 point font.
correction!
> although i've been collaborating with him; his priorities for the zorchpad
should be
> although i've been collaborating with them; their priorities for the zorchpad
you ask what i mean about programmer productivity. consider this python code from https://norvig.com/spell-correct.html https://norvig.com/spell-correct.html:
def edits1(word):
"All edits that are one edit away from `word`."
letters = 'abcdefghijklmnopqrstuvwxyz'
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [L + R[1:] for L, R in splits if R]
transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R)>1]
replaces = [L + c + R[1:] for L, R in splits if R for c in letters]
inserts = [L + c + R for L, R in splits for c in letters]
return set(deletes + transposes + replaces + inserts)
in seven lines of code, it computes all the potentially incorrect words that can be made from a given correct word with a single edit. so, for example, for 'antidisestableshmentarianism', it returns a set of 1482 words such as 'antidisestauleshmentarianism', 'antidisestableshmentarianlism', 'antidiseitableshmentarianism', 'antidisestablesjhmentarianism', and 'antidiseptableshmentarianism', totaling 42194 bytes. how would you do this in uxntal?
here's another part of norvig's program. this part tabulates the case-smashed frequency of every word in its 6-megabyte training set (which presumably consists only of correctly spelled words):
import re
from collections import Counter
def words(text): return re.findall(r'\w+', text.lower())
WORDS = Counter(words(open('big.txt').read()))
this takes about 340 milliseconds one core of on my laptop here, which runs at about 6000 MIPS, so it would take about 34 seconds on a machine running at 60 MIPS, maybe a little longer on the apollo3. there are 32198 distinct words in the training set, totaling 244015 characters; the most common word ('the') occurs 79809 times, and the longest word ('disproportionately') is 18 characters. so plausibly you could represent this hash table without any compression in about 500k, though cpython requires about 70 megabytes. ram compression could plausibly get those 500k down to the 384k the apollo3 has without trying to swap to offchip flash
finding the best correction for a word requiring two corrections like 'slowlyyy' takes 70ms, so plausibly it would take 10 seconds or so on the apollo3. (you could maybe do this in the background in a text editor.) (if it were compiled to efficient code, it would probably be closer to 300 milliseconds on the apollo3, because cpython's interpretive overhead is a factor of about 40.) 'disproportionatelyyy' takes 370ms. here's the rest of the correction code:
def P(word, N=sum(WORDS.values())):
"Probability of `word`."
return WORDS[word] / N
def correction(word):
"Most probable spelling correction for word."
return max(candidates(word), key=P)
def candidates(word):
"Generate possible spelling corrections for word."
return (known([word]) or known(edits1(word)) or known(edits2(word)) or [word])
def known(words):
"The subset of `words` that appear in the dictionary of WORDS."
return set(w for w in words if w in WORDS)
def edits2(word):
"All edits that are two edits away from `word`."
return (e2 for e1 in edits1(word) for e2 in edits1(e1))
note that this requires you to have two such `edits1` sets in memory at once, though you could plausibly avoid that problem by tolerating more duplicates (double letters provoke duplicates in deletes, transposes, and replaces)
norvig doesn't tell us exactly how long it took him to write the code, but he did it in a single airplane flight, except for some minor bugs which took years to find. more importantly, though, it's very easy code to read, so you can easily understand how it works in order to modify it. and that's the most important factor for programming productivity
here are some things in this code that are more difficult to write and much more difficult to read in uxntal:
- managing more than 64k of data (uxn's memory addresses are 16 bits)
- dynamically allocating lists of things such as the (left, right) tuples in splits
- dynamic memory allocation in general
- string concatenation
- eliminating duplicates from a set of strings
- iterating over the words in a text file
- generating a sequence of outputs from a sequence of inputs with a filtering predicate and a transformation function [f(x, y) for x, y in xys if p(x, y)]
- generating a lazy flat sequence of outputs from a nested loop (return (z for y in f(x) for z in f(y)))
- hash tables
- incrementally eliminating duplicates from a sequence of candidates that turn out to be valid words (set(w for w in words if w in WORDS))
- counting the number of occurrences of each string in a lazy sequence of strings
- floating-point arithmetic (which would be fairly easy to eliminate in this case, but not in many other cases; this deficiency in uxn is especially galling since the apollo3 has fast hardware floating point)
- finding the highest-rated item of a lazy sequence of candidates according to some scoring function
and all of that is on top of the general readability tax imposed by postfix syntax, where even figuring out which arguments are being passed to which subroutine is a mental challenge and a frequent source of bugs
note that these are mostly not deficiencies you can really patch with a library. i didn't mention that the program uses regular expressions, for example, because you can certainly implement regular expressions in uxntal. they're things you probably need to address at the level of language semantics, or virtual machine semantics in the case of the address space problem. and they're not tightly tied to cpython being implemented grossly inefficiently; pypy implements all the necessary semantics, and common lisp and c++ have similar facilities in most cases, though their handling of lazy sequences is a weakness that is particularly important on hardware with limited memory like the apollo3
so that's what i mean when i say that uxn is designed to make easy things hard, rather than making hard things easy
you say:
> the pain point might be intentional nudges away from making things the designer doesn't like
the thing is, i don't really care whether rek and devine think that autocorrecting misspellings is a bad thing to do; i want the computer to be a means of expression for my ideas, indeed for everyone's ideas, not for the ideas of a singular designer. that's the apple walled-garden mindset, and it's anathema to me. and, though i could be wrong about this, i think rek and devine would probably agree
> Counting mW and decades implies text > GUI.
so, i don't want to be unkind, but i think this is a misconception, or a pair of misconceptions, and i think i know where one of them comes from. i'll try to steelman it before explaining why it's wrong
the first assertion is that counting milliwatts (or actually microwatts in this case) implies that text beats guis. the second assertion is that counting decades implies that text beats guis. i have no idea where the second assertion comes from, so i'll focus on the first
a first plausible justification for the first assertion is that power usage (over leakage) is proportional to the number of toggling bits (or perhaps cubic or quadratic in the number of toggling bits if voltage scaling is an option) and a character generator toggles many fewer bits than all the computation needed to draw a gui, so applications written for a character-cell text terminal can use much less power than gui applications. a second plausible justification is that we have many examples of applications written for character-cell text terminals which use much less computation (and therefore power) than any existing gui alternative
the first justification has two problems: first, it's comparing solutions to the wrong problem, and second, character-cell text terminals aren't even the best solution to that problem; they're the best solution to a significantly different problem which we don't have
the problem that the first justification is trying to solve is the problem of generating a video signal that is useful according to the goals of the application. in that context, the character generator is running all the time as the beam refreshes the phosphors on the crt over and over again. it is true that the character generator can compute the pixels to send to the crt much faster and with less transistors than a cpu can. but in the zorzpad, the display itself is bistable, like the plato gas plasma terminals from the 01960s. there's a flip-flop printed on the glass in amorphous silicon next to each pixel. so we don't have to consume any computational power refreshing a static display (though we do have to reverse the polarity to it a few times a second to prevent gradual damage to the lcd). we only have to send it updated lines of 400 bits. we don't have to generate a continuous video signal
moreover, the fact that a character generator would toggle less bits than the cpu to generate a line of pixels is somewhat irrelevant, because ambiq doesn't sell a character generator chip, and a character generator implemented in conventional cmos from another vendor (or on an fpga) would use orders of magnitude more power than the subthreshold-logic ambiq microcontroller, so we'd have to do the character generation process on the cpu anyway. at that point, and especially considering that the memory-in-pixel lcd display hardware doesn't impose any timing constraints on us the way an ntsc or vga signal does, there's no extra cost to generating those lines in a more flexible way. even without a framebuffer, generating lines of pixels on demand, we could support glyphs positioned with pixel precision rather than character-cell precision, proportional fonts, multiple sizes, polygons, rectangles, stipple patterns, overlaid sprites, and so on
also, though, generating a line of pixels or a vga video signal from a framebuffer requires less bit toggling than generating it from a character generator. with the framebuffer you literally just read sequential words from ram and send them to the display with no processing. what character generators save isn't power; it's memory. with a character generator you can drive a 1280×1024 display with an 8×16 font (256 characters, say, so 4096 bytes of font memory) displaying 64 lines of 160 columns, requiring 10240 bytes of glyph indices, for a total of 14336 bytes of memory. for the traditional 80×25 you only need 2k of modifiable memory and a font rom. and you can do it with strictly sequential memory access (many early character-cell text terminals used delay-line memory or shift-register memory) and meeting the strict hard-real-time guarantees required by the crt hardware being driven
in this case, 400×240 1-bit pixels would require 12000 bytes of framebuffer per screen, and 24k is about 6% of the apollo3's total 384k of sram. in theory the apollo3 needs about 2.1 cycles per 32-bit word to copy data into the framebuffer without rotating it, a number i haven't tested, so 12600 cycles to redraw both screens from scratch; at 48 megahertz that's about 260 microseconds, so it doesn't use much of the chip's available horsepower even at submilliwatt power levels. the display datasheet only guarantees being able to draw two megapixels per second, so redrawing the screen should take 48000 microseconds, although others have reported that it works reliably at three times that speed. hopefully i can use the cpu's built-in spi peripheral to spew out buffered spi data to the screen while the cpu goes back into deep-sleep mode. if i use a character generator implemented in software to avoid having a framebuffer, i have to wake up the cpu periodically to generate new lines of data, using more power, not less. (which might be worth it for the memory savings, we'll see.)
even without enough memory for a framebuffer, though, character generators weren't the only way to generate hard-real-time video signals. the nintendo (nes) only had 2k+256 bytes of vram, for example, and you could do pretty elaborate graphics with that. it used tiles (which are sort of similar to font glyphs and can in fact be used for font glyphs) and sprites (which aren't), plus scrolling registers. some other hardware supported display lists of straight lines instead of or in addition to glyph indices
the second justification is basically a cohort confounder. yes, vs code uses a lot more cpu than vim. but vim was originally written for the amiga, and vs code is written in electron. applications that run in character-cell text terminals do empirically tend to be a lot faster than gui applications, but that's largely because they're older, and so they had to be written with more attention to efficiency. remember that geos provided a standard wimp interface on the commodore 64, with mouse pointers, sliders, pulldown menus, proportional fonts, overlapping windows, and so on, and all that in color without a color framebuffer, adding extra work. it was slow, especially when it had to load data from the floppy drive, but it was usable. the commodore 64 is about 0.02 dhrystone mips (https://netlib.org/performance/html/dhrystone.data.col0.html https://netlib.org/performance/html/dhrystone.data.col0.html) and the apollo3 is about 60, so running something like geos on it at 10× the commodore speed ought to use about .3% of its full-speed cpu. or 3% if it's running in a simple bytecode interpreter
so that's why i think submilliwatt computing doesn't require abjuring the gui
does that make sense? have i just totally misunderstood you?
you ask:
> Do you favor a specific structure of hardware, OS code, and user scripts, or whatever ends up offering the best use of power and long-term device durability?
well, i think it's critical for the zorzpad to be able to rebuild its own operating system code, because otherwise it's dependent on external computers to modify its own operating system, and the external computers might have become incompatible or untrustworthy at some point. this poses tricky problems of recovery from bad operating-system updates; probably having multiple redundant microcontrollers in the device is the best option, so that on bricking one of them, i can restore its old configuration using the other
ideally, though, the core code capable of bricking it would be a small tcb, not everything that runs on the system the way it is in ms-dos or templeos. that kind of isolation is usually implemented with an mmu, but the apollo3 doesn't have an mmu, just an mpu. so currently my plan is to implement it with a low-level virtual machine instead; initially an interpreter (my prototype seems to have about 10× interpretive overhead, but probably providing a small number of extra primitives will be adequate to eliminate the worst hotspots from things like updating the display and digital signal processing) but maybe later a simple jit compiler. like most microcontrollers, the apollo3's memory/throughput quotient comes to about a millisecond, rather than the second or so that has been typical for human-interactive computation for the last 60 years, so i suspect i'll be leaning pretty hard to the side of sacrificing speed to reduce memory usage
in addition to providing the protection needed to write experimental code without risk of bricking the machine, the virtual machine can also provide services like virtual memory (including compressed ram), transactional concurrency, and transparent persistence
the nand flash chips i'm using (s34ms01g2) are supposed to consume 27 milliwatts when running, but their power budget at the nominal 1-milliwatt full power is only 300 microwatts. at full speed they're capable of 133 megabytes per second, so at 300 microwatts they might be capable of 1.5 megabytes per second. i can't write to them nearly that fast without wearing them out early, but i can load data from them that fast. in particular that means that the 24 kilobytes of a full screen can be loaded from flash every 16 milliseconds, so you can do 60fps video from flash. reloading all 384k of ram, for example to switch to a completely different workspace, would take a quarter of a second. i feel like this is enough of a quantitative difference from the speed of floppy disks on a commodore 64 or a macintosh 128 that it becomes a qualitative difference, and for applications like the spell corrector example i gave in the other comment, it might be possible to use the flash almost as if it were ram, using the ram as a cache of what's in the flash
but maybe when i gain more experience with the hardware, all that stuff will turn out to have been the wrong approach, and i'll have to go with something else
you write:
> I have a radical idea which might not be a good one:
> 1. A "Full" display of W x H characters (the Sharp display or whatever you replace it with)
> 2. A "Fallback" 1 or 2 line display (2nd line reserved for editor info / UI?)
i've thought about this, especially with respect to e-ink screens. some versions of the barnes & noble nook work this way: there's a small color lcd touchscreen for interactive stuff, and a large black-and-white e-paper display for bulk information display. i thought that a small reflective lcd like cheap scientific calculators use would be enough for a line or two of text, permitting rapid interaction without putting excessive demands on the e-paper's slow and power-hungry refresh
but i think that sharp's memory lcds are so much better than e-paper that there's no real benefit to that approach now
> A single 1-bit editable line is so hard to work with that ed[2] isn't even installed by default on many Linux distros. However, people did once got real work done with it and similar tools.
it's not that command lines are hard to work with; it's that ed's user interface is designed for 110-baud teletypes, where erasing is impossible, and typing out a 10-character error message means forcing the user to wait for an entire second, so it's preferable to just say '?\r\n'. user interfaces designed for more capable hardware, such as an adm-3a connected over a 1200-baud modem, can be immensely easier to use, without abandoning the command-line paradigm. you'll note that ex(1) is installed by default on every linux distribution except alpine (which does, oddly enough, have an ed(1)), and i know a programmer who still uses it as his preferred editor, preferring it even to vi
i've certainly done plenty of real work with tcsh and bash, where the primary user interface paradigm is editing a single line of text to your satisfaction, submitting it for execution, meditating on the results, and then repeating. not to mention the python repl, gdb's command line, octave, units(1), r, pari/gp, sqlite, psql, swi-prolog, guile, ngspice, etc. i like direct-manipulation interfaces a lot, but they're harder to design, and especially hard to design for complex problem-solving activities. it's just that correcting a typo a few words ago isn't really a complex problem-solving activity, so, by my lights, even a relatively dumb
direct-manipulation text editor can beat a command-line editor like ex most of the time.
especially if their bandwidth to your eyes
is measured in megabits per second rather than kilobits per second
> you'll note that ex(1) is installed by default on every linux distribution except alpine
On my system, it seems to be set up as an alias of vim ("Entering Ex mode"). Huh. A bit more reading after that has been an interesting historical detour.
> i know a programmer who still uses it as his preferred editor, preferring it even to vi
I've never tried ed before this comment. Others might see it as strange and clearly teletype-oriented, but I think it's refreshingly explicit about every action taken. Even more than ex, it's a vim ancestor where commands and state are persistently visible instead of being hidden.
Thank you for mentioning this, but now I'm a little worried I might start wanting to use ed too.
> i've certainly done plenty of real work with tcsh and bash, where the primary user interface paradigm is editing a single line of text to your satisfaction, submitting it for execution, meditating on the results, and then repeating.
I think you've hit on something important here. Web-oriented workflows with live reload (and Flutter[1]) inherit this. I think it means the form of the REPL itself isn't what we should be focusing on, but easy iteration and maybe cached results to save power and time.
On the topic of accessibility, do you have any opinions on Hedy[2] and whether a long-lived, low-power system might have room for something like it? The site is annoyingly JS-filled, but the idea is interesting: a language where the names in the symbol table are more overtly a user-localizable value.
[1]: https://flutter.dev/ https://flutter.dev/
[2]: https://www.hedycode.com/ https://www.hedycode.com/
> You may be pleasantly surprised by how many people will show up ...
that would be great! on the other hand, i don't want to count on it; i want it to be a pleasant surprise :)