Show HN: A Lispy, embedded Forth
github.com
github.com
Here's another interesting concatenative language in development that I'm following: http://kittenlang.org/
In particular the object/class system of Factor was baffling to me. The paradigms just don't fit together well.
I also think Lisp has an awkward object system(s). If you want that paradigm, I think a dedicated language is better.
object.foo.bar
is apt to read more like this: (class2-bar-of (class1-foo-of object)) (bar (foo object))
I mean, I understand that "a.foo" (class A) does not represent the same name as "b.foo" (class B) due to scoping rules, and so logically you would have to use (a-foo a) and (b-foo b) if you wanted to replicate the same in CLOS. But on the other hand, FOO can be generic and applied to different types of objects. You might even have a mixin providing the FOO slot, which classes A and B inherit from.I find the notation fine, it looks like regular function application (some people prefer threading macros, I personally don't care).
Note also that CLOS allows you to add code "between the dots" by specializing accessors on qualifiers (:before, :after, :around), which can be useful to add orthogonal things like logging, persistency, ...
(libraryA:bar (libraryB:foo object))
Since the package system is not hierarchical and doesn't allow local aliases, if you're unlucky you get fully qualified Java-convention package names and end up writing: (com.myfavoritecorp.dept1a.project7.subprojectX:bar
(com.biggercorp.dept32.project2.subprojectE:foo
object)I know what I would like it to do. There are some additional features that people would like to use (https://github.com/fare/package-renaming), but even though the package system could be improved, I don't think it is so bad right know. It can be frustrating to try to use it in a way it wasn't designed.
I seldom use qualified symbols, so it is generally "(bar (foo x))" anyway. The case when fully qualifiable symbols might be the best solution is when you need symbols with the same name from different packages at the same time (without shadowing), which is quite rare IMO. Also, defining short packages as a facade for other packages is possible. When you develop an application, not a library, you can also choose to mess things up by renaming packages and adding nicknames.
By using unqualified packages, I can change the meaning of the code by editing only the package definitions (and recompiling): you code says `(foo x)` and originally meant `(a:foo x)`, but then you change a package and now it means `(b:foo x)`. For example `b:foo` could be a wrapper around `a:foo`.
Packages prefix help a little when trying to understand where a symbol comes from. For example, "json:decode" is a little easier to read than "decode". However, with the right editor (or just the REPL) you point to the symbol and have the information.
I'm pretty sure that it is, and that generally in my package I would import bar from libraryA & foo from libraryB and not have to qualify the names.
It's only in the case that both packages export a symbol with the same name that one is forced to disambiguate.
I'd say that Lisp's package system is very close to perfect — it really does work well.
Firstly, the exported symbols in one package can be made visible in another package through package use. Secondly, a symbol from package A can actually be made present in package B via import: http://clhs.lisp.se/Body/f_import.htm
It might be up your alley, since you're working on a package-related reader macro:
(Regarding local aliases, I was talking about local package name aliases, which could make it somewhat less awkward.)
Note that the lisp equivalent of foo.bar(x).baz(y) would be something like:
(send (send foo :bar x) :baz y)) which can be made even more terse with a macro, doing something like (@ foo (:bar x) (:baz y)). This was how most pre-CLOS object systems in lisp worked, and CLOS supplanted this.
Implementing SEND in CLOS is trivial.
There are many people who experienced SEND style object systems and CLOS but I haven't found any of them to be detractors of CLOS, which is what prompted my original question; lisp had something much closer to smalltalk before switching to CLOS and if there is a group of lispers who want to go back to that, they are in a small minority.
object.foo.bar
similar as 1+2+3
The later would be (+ 1 2 3)
in Lisp.Thus slot access could be written as:
(? object foo bar)
All we need would be a macro ?, which is easy.Alternatively we can write a reader macro such that we can write:
#?object.foo.bar
The problem with short names and possible package prefixes remains: (? w1 ws:panes ws:background-color color:name)
vs. #?w1.ws:panes.ws:background-color.color:name
vs. (color:name (ws:back-ground-color (ws:panes w1)))Collisions occur under inheritance. Example: you would like to add a member foo to a base class. The class is widely derived in lots of code, not all of which you even have access to, which might be injecting its own foo. C++ partially deals with this with hacks, like derived identifiers shadowing base class ones (which is totally non-OOP and breaks for virtual functions, which cannot be non-OOP by their nature). C++ partialy deals with the virtual function issue by treating the type signature as part of the name, so that foo(int) and foo(widget&) are different virtuals.
Collisions could be a problem even if there is just one class, but its contents are controlled by several parties. Every group has to watch out that they aren't reusing any other group's symbols in that class.
Any sort of automatic mechanism which can inject symbols into a class (e.g. in support of aspect-oriented programming or whatever) will run into a clash, unless it uses gensyms, which is inconvenient if the intent is to inject names that are to be publicly known and referenced.
Object systems crammed into Forth will always be baffling, the fact that it's doable doesn't make it a good idea.
I don't really buy into OOP at all; but i strongly prefer CLOS to alternatives, including Smalltalk; as it let's me reuse pieces of functionality without dealing with the rest.
Using a fundamentalist language as a foundation will always come back to bite you from my experience. I prefer my foundations as general purpose as possible, which usually means C or Common Lisp.
I'm not really familiar with Forth, but my impression from writing a little bit of postfix code was that some pattern matching would make e.g. writing a large conditional much easier.
TYPE: Tree
| Branch(Tree,Tree)
| Leaf
;
:: depth Tree -> Int ;
: Branch($,$) depth {depth} dip depth + ;
: Leaf depth 1 ;
$ is extract, so the pattern Branch($,$) pops the top of the stack and pushes the 2 members. Types and type constructors have to start with a capital letter, but I like the : Pattern word definition ;
syntax a lot.How are you quickly and casually going to drop in a scripting language into any project, if it requires Common Lisp?
Maybe this means "into any CL project". But why would you need a scripting language in a CL project.
Transcript show: 'foo'; cr.
In this case the output of "Transcript show:" is the Transcript object, which is used as the sender for cr.Your Smalltalk example has a lot more special syntax going on, in a stack-based concatenative syntax it might be written as
Transcript ' foo' show cr
assuming appropriate primitive programs "Transcript", "'" (for literal quoting), "show" and "cr"Edit; I use Lisp in the form of Clojure daily as well as Forth for embedded. When writing embedded code, instead of assembly (check my profile, the stuff we use definitely has no room for Node but often not even C) I usually port a Forth if one is not available, at least for testing but usually it is good for production too.
If you are not that interested in the implementation of the compiler (and if so you might be missing the ultimate point of Forth), Starting Forth [2] is a classic resource.
Edit: formatting
From Chuck Moore, the father of Forth, "1X Forth" ( http://www.ultratechnology.com/1xforth.htm ) is worth reading before and after Thinking Forth.
https://github.com/EtchedPixels/FUZIX/blob/master/Applicatio...
It's not well commented, but it should be easy enough to read. It passes the basic ANS Forth test suite.
The C file is also an exciting self-modifying shell script with embedded awk and Forth parts.
Prolog is another one of my favorites, you should check that out.
I sometimes have to code in Python (there's always Hylang!), but all of my personal interest stuff is in the other three. I am currently going through the 4th edition of "Fractals, Visualizations and J", which is a great introduction to the J language and graphics.
I am trying to make a livecoding (toplap.org) environment in J similar to oK, but more musical and with webgl shaders [1,2].
I'm convinced the proliferation of using GPUs, distributed processing, and multi-core CPUs are all a good fit for a true array or vector-based language like APL, J, and K. I get more of a functional high on J than in Haskell, and feel like I am writing Math equations after memorizing the 'words' of J, more like operators (nouns, adverbs, conjunctions, verbs, etc...)
Shen is a great mix of Lisp and a typed (optional), lazy (optional) language with great pattern matching that has been implemented on a lot major programming platforms (Python, Ruby, JavaScript, Scheme, Haskell, Common Lisp, Clojure, JVM, and Emacs Lisp) because it runs atop a small set of 46 primitives called KLambda similar to the original Lisp 1.5. If you implement KLambda, and pass the tests, in a given PL, you can then run any Shen program on that platform [3].
[0] jsoftware.com
[1] https://github.com/JohnEarnest/ok
[2] http://johnearnest.github.io/ok/index.html
[3] http://www.shenlanguage.org/just load the binary into serial flash, point the interpreter at it, and of it goes.