Quantcast
Channel: Hacker News 50
Viewing all articles
Browse latest Browse all 9433

μLithp

$
0
0

Comments:"μLithp"

URL:http://fogus.github.com/ulithp/


classLisp

Ruby is one of those kinds of languages, so everything starts with a class declaration. Kidding aside, I thought it would be cool to encapsulate Lispiness in a class a provide extension via inheritance, but never got around to trying it.

definitialize(ext={})

The constructor seems like as good a place as any to declare the basic functionality. Every minimal Lisp requires a basic set of features, including, but not limited to the following functions and special forms:

  • label : a way to bind a value to a name
  • quote : avoids evaluation of an expression
  • car : retrieves the first element in a list
  • cdr : retrieves all but the first element in a list
  • cons : constructs a new list from some element appended to the front of some list
  • eq : checks the equality of two values
  • if : checks the truthiness of an expression and evaluates one of two branches depending on the result
  • atom : checks if a value is a base value (i.e. not a composition type like a list)
  • λ : creates a function

I'll give more detail about each below.

@env = { :label => proc { |(name,val), _| @env[name] = eval(val, @env) },

Every Lisp evaluation takes place in the context of some environment. In μLithp the environment is a simple Hash instance and represents a global evaluation context. The :label symbol is bound to a lambda that takes two things, an Array with a symbol representing a name and a value and also a context (that is ignored). The name is then inserted into the global environment with the given value. As you can see, all bindings are created explicitly in @env. You'll notice that I created the :if logic with the proc form. Doing so signals to the evaluator that the thing defined should receive its arguments unevaluated.

You'll notice that all of the callable's take an array as the first argument and a context as the second. This is key for future expansion. Additionally, I like to use Ruby's destructuring format to unwrap the actual arguments hidden in the passed array.

:car => lambda { |(list), _| list[0] },

The :car form returns the first element of its single argument. In μLithp, lists are just Ruby arrays at the bottom.

:cdr => lambda { |(list), _| list.drop 1 },

The :cdr form takes a Ruby array and drops the first element.

:cons => lambda { |(e,cell), _| [e] + cell },

The :cons form takes some value and plops it onto the front of the array provided as the second argument.

:eq => lambda { |(l,r), _| l == r },

The :eq form takes two arguments and compares them for equality.

:if => proc { |(cond, thn, els), ctx| eval(cond, ctx) ? eval(thn, ctx) : eval(els, ctx) },

The :if form is the first that uses the eval method. That is, it takes a value as its first argument, tests its truthiness and either evaluates its second argument if true or evaluates its third if false.

:atom => lambda { |(sexpr), _| (sexpr.is_a? Symbol) or (sexpr.is_a? Numeric) },

The :atom form tests for atomness (cannot be split). Currently, the only two things that are atoms are Symbols and Numbers. Numbers are a bit gratuitous because μLithp has no math operators.

:quote => proc { |sexpr, _| sexpr[0] } }.merge(ext)

The :quote form just returns its first argument outright; without evaluation. Also, if an extension Hash was given, it's merged into the global environment.

end

And that's it for the primordial μLithp functions! You might notice that I've not handled the λ form. This little nasty is handled via voodoo later on.


Viewing all articles
Browse latest Browse all 9433

Trending Articles