h3rald
min does not have a C FLI essentially because it has a "Nim FLI". It is really easy to add new symbols to the language using a Nim API (which is also used internally to define all symbols of the language).
See https://min-lang.org/learn-extending/ for more info. Maybe I should generate some Nim docs for the public API though... but it's really easy to use, and you can check the lib folder of the repo for examples: https://github.com/h3rald/min/tree/master/minpkg/lib
The feature set is pretty similar, but I focused mainly on the free form/nosql-ish aspect of it, and although I added support for server-side JavaScript middleware I didn't include authentication OOTB (although it could be implemented or you can integrate with an OAuth2 system - it can validate JWT tokens).
And yes, LiteStore's web app hasn't been updated in years and overall your project looks much more polished than mine.
Good job! Bookmarking ;)
https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Mac...
Basically it's like defining simple functions with parameters.
And here is a section about transclusion, which is a fancy tech writing word to say "include text from another file":
https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm#Tra...
- HastyScribe[1] -- an opinionated markdown compiler that supports advanced features for technical writing like macros, fields and transclusion. - HastySite[2] -- a highly customizable static site generator based on HastyScribe and min[3], another project of mine (and a pretty deep rabbit hole to go into, if you like unusual programming languages)
Examples can be found in the docs listed for most of my with my projects, here:
The only thing missing from those is search, but I could plug in LiteStore[4] and be done with it. OK, I think that's enough self-promotion for one comment, but you did ask...
[1] https://h3rald.com/hastyscribe/
The municipality of Triora, a beautiful historical town now nearly abandoned, very close to the Ligurian coast in Italy, is selling houses as low as 1 (one!) euro, in an attempt to re-populate the area and get someone to invest in restoring the historical landmarks...
https://1eurohouses.com/triora-imperia-liguria/
Honestly, this is a real bargain considering what this place is like and its historical significance.
Granted, you have to commit to restoring the place, but still!
- fae [https://h3rald.com/fae] · a minuscule find and edit utility
- h3 [https://h3.js.org] · an extremely simple javascript microframework
- hastyscribe [https://h3rald.com/hastyscribe] · a professional markdown compiler
- hastysite [https://hastysite.h3rald.com] · a high-performance static site generator
- herald [https://h3rald.com/herald-vim-color-scheme] · a well-balanced vim color scheme
- litestore [https://h3rald.com/litestore] · a minimalist nosql document store
- min [https://min-lang.org] · a small but practical concatenative programming language
- mn [https://h3rald.com/mn] · a truly minimal concatenative programming language
- nifty [https://h3rald.com/nifty] · a tiny (pseudo) package manager and script runner
- nimhttpd [https://h3rald.com/nimhttpd] · a static file web server
I think skruv comes with even bigger batteries (SVG support, Shadow Dom integration and additional vdom related apis), and I am curious to see how they all play together. The one thing that does seem a bit weird is the html helper functions... like Mithril, I went straight for hyperscript: I think it is very underrated right now but it is actually a good way to write a dom tree -- and you also get JSX support (nearly) for free.
Anyhow, keep up the good work, I think the world needs more micro frameworks like these: powerful but simple enough to be understood and extended easily if needed.
[1]: https://h3.js.org
- https://h3rald.com/litestore if I need a RESTful searchable data store and some relatively simple middleware logic.
- https://h3.js.org if I need to write a single-page application and I just want to get things done fast.
- https://hastysite.h3rald.com if I need a static site generator.
- https://min-lang.org for more complex things. It took a bit to get used to it, but I managed to get quite productive with it in the end.
I do occasionally try out more traditional things or the latest stuff but... using my own code feels a lot more gratifying. If I needed something different, I found myself building it from scratch exactly like I wanted it.
min is actually mainly an interpreted language, BUT it can actually as of recently be transpiled to Nim (which in turns generates C code which can be compiled), so you can actually create executable files from min, which is pretty cool.
Adding this form of compilation was actually really easy because in a stack-based language there's essentially one instruction: push an item on the stack... the only things I had to add was wrapping external files in functions to delay their evaluation to when they are required.
Combinators like linrec etc are no different from ordinary operators, they are pushed on the stack and they rearrange it...
Consider the following program that takes an integer as input and prints its factorial:
args
(compiled?)
(0)
(1)
if get int
(dup 0 ==) (1 +)
(dup 1 -) (*) linrec
puts
Note how I am getting the first or second argument from the command line depending if I am running the program through the interpreter (min factorial.min 5) or as a stand-alone executable (./factorial 5).When running the min "compiler":
min -c factorial.min
...the following Nim code gets generated. As you can see, it's mostly just pushing items on the stack :) import min
MINCOMPILED = true
var i = newMinInterpreter("factorial.min")
i.stdLib()
### factorial.min (main)
i.push MinValue(kind: minSymbol, symVal: "args", column: 4, line: 1, filename: "factorial.min")
var q1 = newSeq[MinValue](0)
q1.add MinValue(kind: minSymbol, symVal: "compiled?", column: 10, line: 2, filename: "factorial.min")
i.push MinValue(kind: minQuotation, qVal: q1)
var q2 = newSeq[MinValue](0)
q2.add MinValue(kind: minInt, intVal: 0)
i.push MinValue(kind: minQuotation, qVal: q2)
var q3 = newSeq[MinValue](0)
q3.add MinValue(kind: minInt, intVal: 1)
i.push MinValue(kind: minQuotation, qVal: q3)
i.push MinValue(kind: minSymbol, symVal: "if", column: 2, line: 5, filename: "factorial.min")
i.push MinValue(kind: minSymbol, symVal: "get", column: 6, line: 5, filename: "factorial.min")
i.push MinValue(kind: minSymbol, symVal: "int", column: 10, line: 5, filename: "factorial.min")
var q4 = newSeq[MinValue](0)
q4.add MinValue(kind: minSymbol, symVal: "dup", column: 4, line: 6, filename: "factorial.min")
q4.add MinValue(kind: minInt, intVal: 0)
q4.add MinValue(kind: minSymbol, symVal: "==", column: 9, line: 6, filename: "factorial.min")
i.push MinValue(kind: minQuotation, qVal: q4)
var q5 = newSeq[MinValue](0)
q5.add MinValue(kind: minInt, intVal: 1)
q5.add MinValue(kind: minSymbol, symVal: "+", column: 15, line: 6, filename: "factorial.min")
i.push MinValue(kind: minQuotation, qVal: q5)
var q6 = newSeq[MinValue](0)
q6.add MinValue(kind: minSymbol, symVal: "dup", column: 4, line: 7, filename: "factorial.min")
q6.add MinValue(kind: minInt, intVal: 1)
q6.add MinValue(kind: minSymbol, symVal: "-", column: 8, line: 7, filename: "factorial.min")
i.push MinValue(kind: minQuotation%, qVal: q6)
var q7 = newSeq[MinValue](0)
q7.add MinValue(kind: minSymbol, symVal: "*", column: 12, line: 7, filename: "factorial.min")
i.push MinValue(kind: minQuotation, qVal: q7)
i.push MinValue(kind: minSymbol, symVal: "linrec", column: 20, line: 7, filename: "factorial.min")
i.push MinValue(kind: minSymbol, symVal: "puts", column: 4, line: 8, filename: "factorial.min")What I liked about Mithril was its simplicity: a clean API with a handful of methods but batteries-included at the same time, and very easy to learn, too!
Eventually I decided to write my own micro framework which borrows heavily from Mithril in terms of api design and approach (https://h3.js.org) -- basically, like Mithril, it comes with its own Virtual DOM implementation, hyperscript syntax (imho the best way to write a view, once you get used to it), a minimal router but also more obvious ways to manage local and global state.
Yeah, I am not even thinking about competing with React or other frameworks, as long as it works for me. I have learnt more building a micro framework from scratch about how single page application work than from using React or Angular and constantly checking stack overflow and other docs to make sure I was doing stuff "in the right way".
I would however be curious at what are the limits of such frameworks. Real-time dom updates... depends how complex I guess, and how fast. It would be nice to have a way to benchmark this properly...
Not many people use it of course, and it's not going to ever become mainstream, but I am using it everyday to perform small tasks and also more recently even to build small APIs for other personal projects. Plus I find that working on your own programming language is a very rewarding experience, and it stimulates creative thinking.
I actually go through phases... I have a few open source projects I keep coming back to every few months to fix issues, add small (or big) features, tweaks etc. the most notable ones are listed on my personal page (https://cevasco.org) -- it almost feels like I have my own very quirky and opinionated software ecosystem :)
And yes, I did create something like this but I wanted to include also a router (like Mithril) and a standardized way to handle state, both global and local... and I ended up with https://h3.js.org -- yep, probably not many people use it but I have been using it for months for personal projects and I keep tinkering to improve a little bit whenever I find I need to polish some rough edges or support additional use cases.
Like others said, I wonder how long it will take for bloat to creep in. For now though yes, I agree that you really don't need much code to build a fully functional SPA. And creating your own micro framework really helps you understand how things really work, without relying on someone else's abstraction.
BUT! I tried to cheat a little bit to make things more readable. Take this example from the min front page, which showcases its concatenative style:
. ls-r
(mtime now 3600 - >)
filter
Can you guess what it does? You might, but what about this: . ls-r :files
((mtime > (now - 3600)) ><) =changed-in-last-hour
@files #changed-in-last-hour filter
Now ok, I went insane with sigils and weird operators but:
- creating variables helps
- using infix notation via the infix-dequote (><) operator makes expressions much more readableWhy would I use this rather than a traditional language? It helps reasoning in terms of point-free function composition and in some case it is faster to work with, i.e. processing rules, pipelines, task sequences etc.
While this is true for nearly all software, I missed being in control of when to upgrade, and I also missed a few little features here and there. The result? I shopped around for it a bit and then decided to roll out my own: HastySite (https://hastysite.h3rald.com) which now powers a bunch of sites of mine like https://h3rald.com and https://min-lang.org
Actually I ended up writing my own markdown processor and my own programming language before doing that (which also needed a Readline/line noise replacement, and wrappers for a regexp and compression library) but... hey, that's part of the fun isn't it? That's the very reason why I love being a programmer: it's not about the final result, it's about how you get there and what you learn along the way.
So kudos to you sir, I wish you all the best and that you build more things yourself.
Regarding indexing json... here's how I do it:
proc createIndex*(store: Datastore, indexId, field: string) =
let query = sql("CREATE INDEX json_index_$1 ON documents(json_extract(data, ?) COLLATE NOCASE) WHERE json_valid(data)" % [indexId])
store.begin()
store.db.exec(query, field)
store.commit()
Basically, SQLite supports indexes in arbitrary expressions and so you can use it in conjunction with json_extract. In this case, field is an arbitrary json path.I then added a REST API in LiteStore to create (JSON) indexes... in that way you can make your queries more performant based on the specific json documents that you store in the data store.
In the past I tested importing dumps of the full HTML and CSS documentation docset from MDN and: - Importing files in a directory is relatively fast: I import batches and it can import hundreds of pages in a few seconds - sadly there's no bulk operation yet to insert arbitrary json documents (unless they are stored as files in a folder), so that's quite slow. - full text search and querying indexed (JSON) properties is fast (double-digit milliseconds for searching several hundred of relatively large documents with paged results)
I do need to do some compared benchmarks with similar data stores though.
Show HN: I made a JavaScript microframework in under 700 sloc
https://h3rald.com/hastyscribe/HastyScribe_UserGuide.htm
OK, you are right that ordinary markdown is insufficient because a lot of functionalities are missing for proper technical writing and above all proper content reuse...
That's why in my tool I started from an already fairly-advanced Markdown flavor (Discount) which already comes with a bunch of proprietary extension... then on top of that I added things like snippets, transclusions, fields, and even simple macros.
Project Page: https://h3rald.com/litestore/
User Guide: https://h3rald.com/litestore/LiteStore_UserGuide.htm
10 more programming languages worth checking out
It is simply amazing to see how it evolved and improved over time. I love the fact that it runs seamlessly on several different platform, that is so fast and so syntactically expressive and elegant.
Way to go guys, Araq, dom, and everyone who made Nim possible! Really glad you are finally getting the momentum and the attention that you deserve. Keep it up!
I always wanted to write manuals and documentation in markdown (or any other lightweight markup language for the matter) but not even a specialized flavor of markdown was able to provide all the features that a technical writer expects from a documentation tools (I am talking especially about block-level formatting and content reuse).
So I came up with HastyScribe (https://h3rald.com/hastyscribe/) and then HastySite (https://h3rald.com/hastysite/).
Basically, I used Discount as a base and added features on top of it. I am now using it for all my sites and all the documentation of my open source projects. And I know of at least one company that is using HastyScribe for their own user documentation.
OK, there's some degree of vendor lock-in, but at least the final result is OK and both tools are open source.
For more info and a preview of what an HastyScribe document looks like, here's the user guide:
- An article explaining why HastySite was built and the main technological choices: https://www.h3rald.com/articles/hastysite/
- The contents of the HastySite site as a user guide, in a single, printable HTML file: https://www.h3rald.com/hastysite/HastySite_UserGuide.htm