everything wrong with free software
"obedience breeds foolishness"
*originally posted:* jul 2021
i wont lie, im not entirely happy with the code for my new editor. it is experimental (as fig itself was from the beginning) and i have done a handful of experimental language projects since fig was created. for example, ive done versions of fig where not every line starts with a variable.
none of the languages i worked on before or after fig (this includes figplus, which has some awesome extra features imo-- including an incredibly flexible pseudo-datatype i really love pulled in from another experiment) are as useful to me as fig is. it simply does exactly what i want it to, more or less.
you can write terrible code in fig, just like you can in python.
believe me-- i learned python 12 years ago, and i only have slightly more discipline with it now than i did back then:
try: print "do this"
except: print "do that"
print "do this"
print "do that"
these days. maybe.
worst of all perhaps, are my choices of variable names. i have improved, only slightly. this is a decades-old bad habit, which i only try to do better about for people im teaching (and i try not to be a total hypocrite about it, but it depends on the day).
dont get me wrong, a lot of "best practices" are on the verge of cult practices anyway, particularly in how they are followed. but some i dont dispute, and those are the ones i should make some effort towards.
i should, at least. i suppose its better to make little improvements sometimes than not bother ever, but it probably depends who you ask. im not proud, but i am happy with some of the effort ive made.
before i learned python, indenting and importing libraries made little sense to me. i mean i could have done it, but it seemed so tedious. python made it into something nice.
here is a really fugly example of what i mean:
plen p len
pbef p left insp
prc plen minus insp
paft "" arr mid 1 0
ifless insp plen
paft p right prc
p pbef plus g plus paft
now insp plus 1 swap now insp
a few things make this ugly. i think the worst part is that it just sits in the middle of a large loop, its code i could be reusing but i was really in a hurry to get this done. this code is not even a week old, but in all honesty i am in no hurry to fix this-- i probably should be.
i cant make this perfect, though there are even aspects of it im happy with, but what should really happen is i should make this into a function that can be called by other modules. the reason i didnt is the amount of time i gave myself to get this online and the number of things that i had to get working that were more important. but that doesnt mean this couldnt really benefit from some improvements.
making this a function would help document what it does.
and here is the thing-- fig doesnt have even 100 commands. you can totally print them all out in a little table, like this:
# ( ) , .
: ; = | arr
arrcurl arreverse arrget arropen arrset
arrshell arrsort arrstdin asc atn
break chdir chr close cls
colortext colourtext command cos date
display divby else end except
fig flineinput for forin fprint
function get graphics hex highlight
ifequal ifless ifmore iftrue instr
int join lcase left len
line lineinput locate ltrim mid
minus mod next nextin not
oct open pass plus print
prints pset python randint resume
return reverse right rtrim sgn
shell sin sleep split sqr
str string swap system tan
textmode time timer times topwr
try ucase val while wend
of the symbols, the only ones that do anything are # for comments, " for strings and . for decimals. anything else is there to help you write more readable code-- depending on your concept of readable.
colortext and colourtext do the same thing, and wend and next and nextin do the same thing.
the parameter count of every command is minimalist and fixed-count, so every command always has the same number of parameters (many commands have none) every time. the exception is function, which is used to define a function (user decides the parameter count).
the structure is also extremely consistent-- left to right, like english. of course if you get bored translating ordered expressions into left to right, you can always use inline python:
t += p - p * x
most modern versions of basic cant be learned this quickly, but fig compiles to python. and those equal signs are optional, you can just use spaces there. (the inline python part follows python rules directly).
some people hate pythons indentation, so in fig its optional. it uses commands instead (fig, next, nextin and wend are the equivalent of unindenting).
what does that bit of code at the top of this article do?
"plen p len" works like this:
* the variable on the left is the working variable, like a named pipe
* first it gets zeroed
* when the second token is a variable, its value gets copied to the working variable
* "len" converts the working variable (as a string) to a numeric representing its length
so basically: plen = len(p)
but you could keep going: plen p len times 5 str plus "hello"
and in python this would be: plen = str(len(p) * 5) + "hello"
but fig doesnt parse that way, for one because when i used to teach basic, some mathphobes said it was "too much like math". of course, all programming is math-- why cant it be a bit closer to other languages though?
oh, for people who LOVE math there are plenty of languages good for that purpose-- like haskell. why cant there be one for people who prefer language?
then there are languages designed to be even more like natural english, and that gets really tedious to code with too:
create a variable named plen
copy the value of p to plen
set plen to the length of plen
when you can just say "plen p len"? no...
but im not against people writing natural-language-based programming languages, theyre just too much to code in imo. what about a cobol-style-like compromise?
copy-value p to plen
get-length of plen as plen
i mean, thats still a lot to say.
the code separates the buffer (p) into the lines BEFORE the insert point (insp) and after the insert point.
the before-insp is called pbef and the and after-insp is called paft.
then it takes g, which is the line to add, and combines them.
then it increases the value of the insert point.
there are so many ways to write this in a clearer fashion. instead, i wrote it in a hurry and didnt even bother making it a function.
thats too bad, because other modules need the same functionality. the .sh command (a separate module) simply borrows the code instead of being able to call it by name.
i could have just done this:
function inserttext buf text insp
then put the code here
then every module that wanted to could use it-- and the code wouldnt be sitting in the middle of a loop that shouldnt have so much code in it without being organised into functions.
i learned to code before i was 10, i didnt NEED to organise anything into functions. and the syntax for doing it was messy, redundant and tedious.
python has a clean, logical and elegant way to define a function, and fig borrows the simplest aspects of that.
but if you see something like that, dont blame the language. it isnt figs fault, i should just write better fig code. its not really that hard, its just more work.
i dont think its really a defence that i at least try more than i used to. learning python helped a bit, and creating fig gave me an additional reason to try more.
im a very lazy coder, and i havent written many programs that have more than a thousand lines.
sometimes i choose a good variable name. i designed a language that made it a lot easier to go back and change a variable name to something better, but i still code in a way that would make it a good idea to, you know, go back and change the variables to something good.
for a small amount of code, you can really use anything. if im really in a hurry, i just use p:
p = "hello world" ; print
i try to use "now" as my "generic everything" variable because it makes more sense in many contexts, and its still super easy to type.
you can get away with a two-letter function name like "ls" in shell code, if you use it all the time. fig has some three-letter identifiers like "arr" (create array) and "cls" because you can type them very quickly.
for a small amount of code, using one or two letters makes it clear "this is a variable, NOT a native command".
but really, once your code is working (or even better before, if it doesnt slow you down too much) then it can be a lot more readable with good variable names.
and dont put too much code in a single loop-- move it out to functions.
this isnt esoteric stuff, i mean i dont think it is at all, its the most common and mundane of the things you can do to START to write better code.
hypocritical version: "do what im suggesting, not what ive done here".
less-hypocritical version: "consider these suggestions, if you want to write better code than i too often do". this at least works as advice to myself, and i dont know how hypocritical it is to document it for the sake of others as well. (perhaps slightly).
the focus and priority of figured, is to move (practically) every feature of the editor (even saving the file) into a separate file that you can either keep or delete. it DOES need to have a chdir command added to the main program though, because you cant move that out to another file without support for it in the main program anyway.
so i feel good about the fact that at least every program will be small. though if id bothered to make the insert_text functionality into a function, it would save adding similar code to every module / feature that needs it.
i should of course, still do this. at the very least, the current versions implementation is useful as a bad example.
the most shameful thing of all though, is that the .q command doesnt work. which is just ridiculous. i tried a bunch of things, i looked at the compiled code, for some weird reason (i still have guesses) it doesnt get to the part where it quits on the first try. that part of the code simply doesnt run.
somewhere theres an extra unindentation, or some other thing that shouldnt happen, though refactoring can be a simple and effective way to find and fix such problems. in the past couple days, i have not done this. but if you happen to look at figured and wonder whats going on:
at least this article does something to explain why. im not entirely ashamed of what im doing here-- i think the concept (of figured) is important, and its useful to me because i want an editor similar to my gui editor, in the environments where i used to run nano. i dont want to bother with ncurses this time, i want to make the display of text "simpler" than that, more like cat.
even the part that gets input could be moved out to a module, then maybe you could even have an editor that doesnt care whether you have ncurses or not (because you could change that by swapping modules out). with the initialisation though... i mean maybe its possible to move that out to a module without explicit support in the main module.
even the chief gnewsance likes the concept, though he would have chosen a different language. i still code in python sometimes, but im enjoying the further exploration of what can be done with fig. its meant to be capable of cool things, even if its an extremely modest and ridiculously simple language. of course importing is not built in, because if youre ready to import things youre probably ready to use a little python code to do it.
you can still wrap that up with a fig function if you want to, but python has such a nice and flexible import feature-- just like it handles complex expressions perfectly well. fig is designed primarily and deliberately for simpler expressions.
but if i had more discipline as a coder, it would result in more readable code-- in fig, as well as other languages. something to think about, at least. i wish i could say it pushed me more to improve in this regard, but it does at least a little. even professionals write a lot of shameful code when theyre in a hurry-- they just write slightly less shameful code.
another thing to note is that inline python code is often unreliable if you indent it with the rest of your indentation-- if you indent loops and conditionals and then stick inline python code in the middle of that, it might work, or it might cause issues.
this is because fig keeps track of indentation level, even with python added, so if you add indented python code youre dealing with the indent level of the python code as well as the separate indent level of the fig code.
i think sometimes it works anyway, but its better to move inline python code to functions, as a rule. its also better for readability, since i move inline python all the way to the left to make it (hopefully) more reliable. if the inline python is already on the left, or if its a one-liner with no indents within the inline python itself, you probably dont need to worry about it.
you CAN add complex python code to fig, absolutely, as well as write complex fig code. if you want it to work well, you should probably move complex python code into a function, so that mixed indentation levels are avoided. this may even prove to be the reason that .q isnt working. ive never had it not exit, but it sometimes takes two or three tries-- thats ridiculous.
its only a few days old, but some of these quality issues are definitely worth addressing. i thought i better even make note of them, lest you think i suppose some of these atrocities are actually good ideas.
as for the overall design... we can disagree, but im willing to defend that much. i put plenty of thought into that, and tended to do everything for a reason, but im not saying that some other design couldnt be better.
part of the reason i made the language itself so ridiculously easy to parse, is i wanted to inspire more people to try their hand making a very simple language, for themselves. if enough people made even a rudimentary programming language, i think it would help them appreciate programming (and programming languages) that much more.
incidentally, i have more fun when im coding in fig. sometimes it helps me get things done, other times inline python is more efficient, really. one of the great things about inline python was that new features never got added to fig unless they were being done in inline python often enough to justify adding a native version. figplus adds features, but fig usually has enough features that i need, that i lean towards the simpler program to get things done.
unlike figplus, fig was also stripped of things like colorama and pygame, due to both being github-based (fig was written years before the purchase of github by microsoft). i consider fig to be up to date, and figplus to be nearly abandoned. but i am happy with both.
im sort of too busy demonstrating what fig can do, to demonstrate how to do it better. my other editor is done entirely in python-- figured is my first partly-successful effort to make something with fig i can actually use to edit text.
it still needs a module to delete lines, at least. i can insert a new line (and this is after just a couple days of coding) and save the file, and run shell commands that output to the edit buffer, but there is presently no way to delete a line. it would take only minutes to create a module that accepts one or two line numbers and removes for example, a single line from the buffer. but a range is probably better, and ive been doing other things for the past couple days.
right now, its a glorified logger-- you can add text and save text. theres not even a module to "open" a file, but since shell can cat into the buffer, it can already effectively open files-- just not without very basic knowledge of the shell.
im sure anybody including myself would prefer to add a few more features to the main program, before calling it complete. it needs chdir (you cant do that from the shell module, people who code programs that have a chdir feature understand why) but for example, if i want it to be possible to write a save module that doesnt require retyping the name of the file on every save, the main module should support that feature (optionally of course) for both open and save modules. it doesnt yet, so the existing save module just makes you name the file every time.
figured is designed to do some cool things and brags about that a little (of course its a deliberately modest program, with a couple of notable ambitions for demonstration purposes) but i wont pretend its where it ought to be, after just a few days.