Whoa, I done been Reddited

Whoa, linky. Who knew people actually read this blog? Zounds.

I should answer some of the questions being asked. (Why here and not on Reddit? Well, I don’t have a Reddit account, for one, and for two, I’d like to keep my answers about Smile in a place where they’re easier to find, rather than buried deep in a Reddit thread somewhere.)

So here we go.

  1. Smile is a Lisp derivative. With all that implies. Under the hood, all code is represented via symbols and cons cells. That’s not an accident; that was one of the original design goals. The magical abilities of the Lisp family to mutate your own code, to treat data as code and vice versa, were one of the original design requirements from when I first started on this trek so long ago. ‘null‘ is the empty list and vice versa. (And yes, ‘null,’ not ‘nil.’ That’s a long explanation by itself.) There is an ‘eval‘ that does what it says on the tin. Methods are functions are lambdas and are represented as lists and constructed from lists, in a very Schemish kind of way.
  2. Smile is a Smalltalk derivative. Cons cells are objects, with all that implies. There is a base Object from which everything descends. (Most stuff prefers ‘Actor’ as its true base, because Object is intentionally empty, whereas ‘Actor’ has a whole truckload of useful methods for doing reflectiony kinds of things like get-property and base and property-names and string? and list? and so on.) There is also a syntax translator that converts “clean” syntax into canonical form, following many of Smalltalk’s rules.
  3. Smile is a Self derivative. There are no classes. Everything is an object. Objects “inherit” in prototypical fashion. This is inspired both by Self’s and by JavaScript’s object models, but a lot cleaner than JavaScript’s funky way of handling objects.
  4. Smile has some similarities to Ruby and to Python, but it is neither. Ruby and Python are great languages. Matz and Guido both rightly deserve praise for what they’ve built. But at the same time, I believe they didn’t go far enough. They’re great practical languages, but they don’t have the theoretical purity of something like a Lisp or a Smalltalk. In Smile I can take a method on an object, pull its body apart as a tree of cons cells and symbols, inject new “code” as data in the middle of it, turn it back into a lambda, and hook it to an object as a new method. (Example provided below.)
  5. Smile is not intended to compete with D, Rust, or Go. The focus is on a dynamic language that is as flexible as Lisp and Smalltalk but with clean, readable syntax. A future Smile interpreter or compiler should have no trouble being competitive, and I’ve been careful to design the language to make life easier for those implementing fast interpreters or compilers, but it’s not statically-typed like D, and it’s not a type-proving pattern-matching functional language either like Haskell or Erlang. Google has shown with V8 that you can have “acceptably fast” and “dynamic” together, and Smile should be easier to perform the type proofs against than JavaScript is; but it will probably never be as fast as C or C++ or C# or Java or D, and that’s okay, because that’s not its goal.
  6. Identifier parsing is unusual, by design. Certain punctuation characters terminate a preceding identifier, and certain others become a part of the preceding identifier. There are two categories of identifier, alphanumeric and punctuative, during lexing distinguished by the class of initial characters each will accept, and each allowing different sets of subsequent characters. (For the compiler geeks out there, these classes are uniquely describable using FSAs, but are more easily implemented with ad-hoc code.) The identifier rules are designed to allow you to write x+1 or sum/count in a natural way, without requiring the spaces that Lisp identifiers would require to disambiguate it.
  7. About hyphens. These identifier parsing rules mean that hyphen is necessarily overloaded; a number of you pointed out that hyphen is a bit odd in some of the examples. print-line is a single identifier, as is x-1. However, print- x (notice the space) is three identifiers because hyphen cannot be an initial or trailing character on an alphanumeric identifier; but on a punctuative identifier, like <--, it’s perfectly legitimate as a trailing character. Requiring whitespace to separate all identifiers is annoying, and I spent quite a lot of time refining the rules over the years until I got a set of rules that would allow it to be omitted in a lot of common scenarios. Also: Why hyphen for separating identifier words and not underscore? Simple: It’s one less keystroke. Hyphen is easier to type than underscore, and surprisingly, I’ve found it to be more comfortable to read too. There are a number of places in Smile where the language design was influenced by the number of keystrokes required to do something, which is (among a few other reasons) why Smile uses if...then instead of if (...) — parentheses are slower to type than then is.
  8. About types in Smile. It’s a dynamic language, duck-typed, and the focus is on dynamic the same way that Lisp is dynamic. That said, functions are allowed to provide type assertions, precondition assertions, and postcondition assertions (all of which throw an exception if violated), somewhat like a more advanced version of what TypeScript does for JavaScript, and those will go a long way toward providing the type-safety people crave and toward providing the type guarantees that a fast interpreter or compiler would require, but those all remain optional across the board. If you want to quickly mush together a big ball of weakly-typed data, go ahead.
  9. Tools are definitely needed. So far, I have a working interpreter, and I’m slowly rounding out the corners where it has obvious bugs or where certain parts of the language spec aren’t implemented or aren’t implemented properly. My day job is spent mostly in C# and JavaScript with a great team of devs building and maintaining a massive application that runs on more than a hundred servers, and Intellisense and Visual Studio’s debugger can definitely spoil you. I grew up on C and vi and a Un*x command line (and I still live at a bash prompt in Cygwin), but I haven’t seen tooling for any other language quite as slick as Visual Studio offers for C#. Smile won’t have that out of the starting gate. It’ll have a command-line interpreter that’s kinda on the slow side, but that works and that at least spits out the correct line numbers when your program borks.
  10. Smile is very immature at this point. The core of the language is there, and I have a working interpreter. The libraries are great in some places (lists, functions, integers, strings), and are still pretty severely weak-sauce in others (exceptions, files, networking, more complex data structures). The interpreter is currently written in C#, and I really want to someday rebuild the whole thing in C for performance and flexibility, but C# allowed me to get it off the ground and fix bugs and gotchas faster than C would have. There’s no debugger. A handful of the ancillary language features are still either partially implemented or unimplemented (first-class macros, assertions, Scheme-like catch-symbol-as-goto). Nearly every other major and minor language out there runs circles around the current implementation. But even with all that, what exists is a surprisingly solid foundation, and I’ve built the first couple of initial “release” packages of it for the first few adopters to begin using — literally, smile-release-0.2.tar.gz is the current “release” tarball.
  11. I don’t expect anybody else to like Smile. Seriously, if you’re a Lisp addict, go code Lisp — I sure as heck won’t stop you. Haskell? Ocaml? Go have fun and knock yourself out. If you want Ruby or Python, they’re fine and worthy languages. Smile exists because none of the many many languages I’ve coded in over the years fit what *I* expected to see in a language. I expected good theoretical foundations, a lack of kitchen-sink mentality (I’m not gluing in features just because they’re fashionable), and high expressivity with just a handful of basic constructs. I expected to not have to work around hardwired syntax for things like print because not everything needs that. I expected functions to be treated with the respect they deserve so that they’re just as much data as “real” data. I expected lots of built-in data structure abilities so that I could mush together data when I wanted to, structure it cleanly when I wanted to, and didn’t have to manually reimplement red-black trees and heaps for the 47,000th time. I expected good string processing abilities so I could finally stop using Perl as my glue language for crunching through text files. I didn’t find all that elsewhere, and I sure looked: Every time a new language popped up, I scoured its documentation for its feel, for its soul, and nothing felt right, so I kept working on Smile until it had become what I’d hoped for years somebody else would build so I didn’t have to.
  12. I don’t expect Smile to change the world. Smile is cool, and I have had more fun coding in Smile than I have had in any other language, and I’ve coded in a lot. But there’s a lot of language competition out there, and maybe Smile will never have anybody using it but me. And I’m okay with that. I’m going a direction I believe is right, and I don’t expect anybody else to follow me — I’m sure as hell not a leader. If you want to code in Smile, that’s awesome. And if you don’t, that’s fine too. You’re welcome to your opinion, and I respect it; but your opinion probably won’t change Smile because the language is what *I* believe it needs to be.

Since you guys asked for it, here’s a slightly more interesting example demonstrating some of what makes Smile unique:

    #include "stdio"

    Integer.factorial = |n| if n > 1 then n * factorial (n - 1) else n

    Integer.sum-up-to = Fn of Integer.factorial.arguments,
        (Integer.factorial.body map-recursive |item| if item == `* then `+ else item)

    Stdout print-line Integer.factorial, ": ", factorial 5
    Stdout print-line Integer.sum-up-to, ": ", sum-up-to 5

Output:

    [$fn [n] [$if [n.> 1] [n.* [[n.- 1].factorial]] n]]: 120
    [$fn [n] [$if [n.> 1] [n.* [[n.+ 1].factorial]] n]]: 15