The Rewards of Creating a Programming Language
mikedrivendevelopment.blogspot.com
mikedrivendevelopment.blogspot.com
It was called, ingeniously enough, "T" (no, not that "T"). As far as I (and cursory Google searches) know, it was never released to the adoring public.
I used lex and yacc (half jokingly referred to as 'ick' and 'yuck') and K&R C for the compiler and VM.
The particular type of testing we were targeting involved writing test cases that would read/write over serial lines to a telephone switch's console program. Therefore, the language needed to have good serial/terminal I/O, and it needed to have amazing string/pattern matching.
I wrote two features that I am still particularly fond of:
- regexps were a built in type. Strings were written like 'hi there' and regexps were written like `hi ..ere` - supported standard unix regexps
- associative arrays. Lots of languages have these now, like python's dict
The cool thing about it was how we tried to allow strings and regexps have a polymorphic relationship at the language level. The statements:
x == 'some string' or x == `some [^t]ring`
would be valid for strings in x, though the regexp had other operators that didn't make much sense with strings. It got really interesting when we combined the regexps with the associative array: dict['hello'] = 4
dict['help'] = 2
dict[`he.*`] == [2, 4] # trueI suppose a trie + glue logic (regexp -> NFA -> DFA) could work, for classic (read: actually regular expressions) regexps at least.
dict[`he.*`] == [4,2] could also hold. Or maybe [...] was a set literal and not ordered list.
Interesting ideas, perlish in spirit, somehow less cryptic.
Was it published ? was there other ideas you could talk about ?
I know nothing was published or released. I actually left BNR/NT less than a year after the language went into production (going to say July, 1990) - I don't really know how long it remained there. Perl was popular, and Tcl was definitely a 'thing' in the lab at the time - the writing was on the wall for home-grown languages.
The world does not need yet another reskin of Java, but it could use new programming paradigms and new ways of solving problems.
As a learning exercise, implementing a language is worthwhile simply to gain the understanding that languages are not magic. To boot, you’ll pick up loads of useful techniques in the realms of parsing, data flow analysis, error reporting, and optimisation.
https://www.reddit.com/r/coding/comments/2ocw2r/how_to_creat...
There's a rule of thumb among language designers that your language should either focus on proving out one big language feature, or it should introduce zero new language features but combine new language features from a number of other languages. So for example:
Erlang introduced lightweight CSP-based concurrency. Go popularized it.
Haskell introduced typeclasses. Go and Rust popularize them (as interfaces and traits, respectively, the latter also influenced by C++ STL's concepts).
Cyclone introduced linear types. Rust popularizes them.
Smalltalk introduced object-orientation. (More precisely, Simula did and Smalltalk took it to its logical conclusion.) Java, C++, and Objective-C popularized it.
Self introduced prototype-based programming. Javascript popularized it.
CLOS (Common Lisp) introduced the meta-object protocol. Python and Ruby popularized it, particularly in their Django and Rails web frameworks respectively.
Usually popularizing a language involves a good deal of work that's not sexy, notably building up a large standard library, developer tools, a package manager, and a whole ecosystem around the language.
This rule doesn't exist among language designers, at least the ones I hang out with. You'll find a mix of innovation and invention in most programming languages.
* http://redmonk.com/sogrady/2015/01/14/language-rankings-1-15... (Haskell slightly ahead in their numeric ranking) * http://www.tiobe.com/index.php/content/paperinfo/tpci/index.... (Golang slightly ahead in their ranking) * Slightly more published books on Haskell that I can find (although obviously it's been around a lot longer)
I have no doubt that Go will eventually surpass Haskell in terms of popularity, and may even have a slight edge, but suggesting that Go popularized typeclasses smacks of fanboyism, and reminds me - in no small measure - of: https://www.youtube.com/watch?v=bzkRVzciAZg
I'm not exactly a fan of Go nor am I a fanboy, quite the opposite these days. I don't have to like go to mention that the way interfaces are used kind of accomplish the same thing as typeclasses.
I also feel that (sadly) Go is more popular than Haskell. Partially because of the number of Haskell programmers I've encountered in real life is much lower than the number of Go programmers I've encountered.
http://stackoverflow.com/questions/2982012/haskells-typeclas...
The main similarity is that 'methods' are defined 'outside' the data type they belong to. But there are a huge number of differences: Haskell's typeclasses are used for many things that don't look like methods at all, while Go's behave more like a classic OOP interface you'd find in Java.
So, perhaps superficially similar-looking, but they work quite differently in practice.
This is a really ironic thing to say considering Go doesn't have generics, rust doesn't have higher kinds, they don't support any sort of implicit (or global) way of using them as constraints.... are you sure you know what type classes are?
As a side note although clunky HKTs are encodable in Rust and I plan on implementing them natively once we have landed a stable version of 1.0.
pub trait SomeTrait { ... }
pub fn some_function<T: SomeTrait>(arg: T) { ... }Why bother? I mean, at some point some bigger "name-brand" language is going to take your feature and subsume it anyway. Why compete for mindshare?
If you really have an idea for some new language semantics, then skip the whole syntax phase: express your language as a DSL in another language you already know, use that DSL to write programs and libraries, and see if it helps you compared to not using the DSL. The great thing about this is that you can still rely on all the features and libraries of the parent language while you're doing this! Your DSL only gives you the option of using the new, extra semantics you wanted to test, where and when it adds a benefit.
Now, of course, this might not work if your idea is for a reduction in or purification of semantics—if, merely by having the option of "calling through" to the parent language, the value of your language semantics is destroyed. But if it's just an idea for a feature? Building an interpreter and defining a grammar and create a whole new ecosystem around it is putting the cart before the horse.
Erlang introduced fault tollerant concurrent programming. With isolated heaps (thus fault isolation) and a framework for supervision and distribution. Nobody has popularized that except Erlang so far.
Besides even if you want to talk about CSP, what Erlang has is "Actor" based programming. And even that fell out organically out of concurrency and fault tollerance rather than through academic channels or academic literature survey. Creators at the time didn't even hear about "actors" only years later someone associated the two together.
They are nothing more than Objective-C protocols that served as inspiration to Java interfaces.
I'm curious - how did you test it for compatibility with the main K implementation?
I don't have a rigorous approach for testing compatibility at present; My starting point was the K2 manual, and I've tried to get as many examples from that working as possible. I have been learning the language as I go, so often I don't realize functionality is missing until I stumble across an existing K program that doesn't work properly.
I'm targeting K5, which is unreleased, so there's a great deal of speculation. I think I have a decent subset of the functionality working now, but most of the time when I write new programs using it I still shake out bugs and unhandled edge cases.
Since you are playing in JS, you might also have a look at c.js (http://kx.com/q/c/c.js), which is the tool KX uses to translate between JS objects and k objects through web socket IPC with a k process (http://code.kx.com/wiki/Cookbook/Websocket). Might be useful to see how Arthur envisioned the k data structure in JS. Also, it would be kind of interesting to have a k5 process running in the browser talking with a k4 process running locally in the 32bit trial...
https://class.stanford.edu/courses/Engineering/Compilers/Fal...
I found SPIM super-annoying, but managed to resist the temptation to build a better MIPS emulator. :-)
Highly recommend: writing a (very simple) compiler is no longer a black-art-seeming thing to me.
https://dirkjan.ochtman.nl/writing/2012/07/21/compilers-on-c...
Afterwards, I thoroughly overhauled my nascent compiler. It currently lives here:
(It's a Python-like systems language, with a compiler written in Python targeting LLVM IR.)
http://research.microsoft.com/en-us/people/smcdirm/managedti...
Co-designing your editor with your language allows for a better experience.
You are right in that all of this is basically undocumented in textbooks. Even their presentation on on parsing is mostly unhelpful (if you want an error tolerant incremental parser for your editor, recursive descent works very well).
Does time itself reveal itself as the horizon of being?
One thing that is cool is how time is related to observation, the same thing that happens in quantum entanglement. If you don't observe the state of an object, time doesn't really exist.
I definitely recommend targeting languages that already have robust test suites. It makes things a lot easier. I was going to do a JS implementation a while ago, and there is a pretty solid suite for that from the ECMA[3], but it's kind of hard to extract. One of the things that made ruby so fun was that the test suite that existed at the time was rubyspec, which is actually written in ruby. Just getting to the point where I could run the specs was a huge moment where the implementation reached a measure of usability.
[1] https://github.com/stormbrew/channel9 -- it's a perennial project for me, it can run an old version of mspec+rubyspec as a ruby 1.8.6 implementation. Note that I skipped the parsing, leaving that to an existing parser written in ruby, because parsing ruby is a bigger project than a ruby-compatible VM.
[2] https://github.com/stormbrew/channel9/tree/master/environmen... -- see class.c9s and object.c9s for the vm specific script the fundamental implementation of language features is written in.
Someone once told me something similar, "always be customer number one".
Writing a language (lexical, syntactic, semantic phase and then code generation) will teach you an incredible amount of things in much less time it would take you to read about these things.
What your language looks like is completely irrelevant, this is strictly about learning from the journey.
And once you've done that, make sure to mention it in your resume: I, for one, will instantly give you brownie points if you mention in your resume that you wrote a language.
I've written little languages (mostly for generating code in other languages, which proved to be an interesting way to explore certain design patterns) but always shied away from going all in on a bigger language. Maybe when I retire...
OWL aims to be a low level object-oriented language without a garbage collector (albeit with reference counting).
The best 'example' program using OWL right now is a game I made for the 48 hour game jam, Global Game Jam 2015: https://github.com/bsurmanski/ggj2015
I agree with the poster that making a programming language from scratch is a great way to explore programming. On top of that, its a great experience seeing able to implement all of those features you wish were in your favorite language (or find out why they aren't).
Pretty cool, never even thought about this being possible.
brand position <- Point
Position is a point, but is not a distinct type. You can document it centrally and not for each time it is used as an argument to a procedure or as a variable. E.g. def Set(position): ...
You have define brands before you can define your procedure, but multiple procedures can share brands they use to declare their arguments.Obvious problem:
def Drawline(position, position): ...
Oops! We now need a position0 brand and a position1 brand, both of which are aliases for Point. brand position <- Point
brand start-position <- position
brand end-position <- position
def DrawLine(start-position, end-position):
... = start-position + ...
... = end-position - ...
But that is kind of annoying, brand composition as adjectives would probably work better: brand position <- Point
brand start
brand end
def DrawLine(start&position, end&position):
... = start&position + ...
... = end&position - ...
Very natural language, and start/end can be used to qualify all variables that are starty or endy. The biggest problem with the approach is that you have to think ahead of time about what your variables mean, and they might become fairly verbose (so "start position" rather than "startp"). Also, when you get into the implementation, it is kind of tiring to think about what every local variable should mean, so maybe this only really works for procedural parameters and maybe to help define fields.I've been thinking about this for awhile, but haven't put it into a prototype yet.
FWIW I've realized that in languages that are variable centric, an anonymous naming convention is often preferable to a human name because it centers one's concerns around the algorithm. After studying the options for a while I now flip between descriptive word style and "single letter and number." So I have a lot of "x0, x1, i0, s0, a0" in my function arguments. Where it's a record type, I'm deciding between abbreviation and descriptive - which ultimately depends on how often and densely I expect the data structure to be accessed(short names imply it gets used in a dense, idiomatic style).
define do_the_thing (Foo bool -> Bar):
-> should_log;
if (should_log):
dup log_foo
foo_to_bar
if (should_log):
dup log_bar[1] https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+b%29+-%3E+%...
I mean, if I write a small function doSomething(inputDataString) then when talking about what the function does, I'd just refer to "the string" or "that string" as there really can be no more meaningful name to refer to that. If there are multiple such parameters, only then names start making sense.
Similarly for iteration constructs like java 'for (Foobar foobar : foobars)' - when you're talking about what is done with that single instance of foobar, you just refer to it as 'a foobar' or 'every foobar', and a specific name is often meaningless if you don't need to distinguish it from others.
Like the demo version, but doesn't lock your browser with infinite recursion, and supports Ctrl+S! PegJS is a really great/fun tool.
My new love is Parsec [1] + doctest [2]. You start building simple little parsers, and build on top of them, plus inline testing makes it easy to write the parsers correctly. Also Haskell's algebraic datatypes and pattern matching make it nice to build and work with the AST. <3
[1] https://wiki.haskell.org/Parsec [2] https://hackage.haskell.org/package/doctest
Also how the first C++ compiler used to generate code.
Writing a compiler is a significant amount of work and should be pre-researched to avoid issues like the above.
Maybe I'm missing something, how many people felt that syntax made an idea pop differently and was part of the understanding ? To me it's often the opposite, it conflates too many things in a few symbols and then you waste time discussing corner cases.
Do not waste your time on syntax, if you are going to create a new language, rather than a parser.
I'm not saying that syntax is not important. But I feel semantics deserves much more attention.
This hit me, what if variable names CONTAIN types? int__i = 1
And of course, it can be made optional. I will much prefer to do optional typing in the var name itself rather than the ugly syntax that guido has proposed with python.
That sentence is in every act of creation - something new just spawns something else that was not possible before the original act.
It's marvellous to see and indicates he is on the trail of something good - all the best.