in which you may get started with bus scheme
source link: https://technomancy.us/104
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
So a number of folks have asked me how they should get started with Bus Scheme.[1]. I've mostly just said silly things like, "Umm... good question. Maybe read/watch SICP?", which is silly because it doesn't have much to do with the Bus part of Bus Scheme, not because The Structure and Interpretation of Computer Programs is silly.
There's a poster in my favourite bookstore that has Dante's Comedy, the Iliad, and a few other classics captioned with something like "Might as well start them now; you're going to have to read them eventually anyway." I hold the same notion regarding SICP and perhaps The Little Schemer, but I could see how it'd be helpful to have an introduction to Scheme from a Rubyist's perspective since reading a book like that can be large-ish mental investment.
Scheme is a programming language directly descended from Lisp. It's most often compared to Common Lisp, which is in some senses its big brother. Scheme is usually considered less "kitchen-sink"-ish than Common Lisp in that it only defines an extremely clean small core language and allows developers to extend it seamlessly to do what they need. In the words of the creators of Scheme:
Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.
(As a potential student of Scheme, you should be encouraged by this notion as it directly translates into fewer concepts to learn.)
Ruby draws a lot of its heritage from Scheme, though Matz does not share the idea that a language should be limited to a very small number of core axioms from which everything else can be defined. [2] Destructive method names ending in "!" and predicates ending in "?" were inspired by Scheme. Matz himself has even lightheartedly referred to Ruby as "MatzLisp". So this is a language that at the core should not feel too foreign to a Rubyist, even if the syntax looks quite different.
Let's dive in. sudo gem install bus-scheme if you haven't got it installed. Go ahead and launch Bus Scheme with the bus executable. Like irb, it drops you into a REPL, or Read-Eval-Print Loop. Scheme programs are made up of expressions. When you enter expressions into the REPL, they get evaluated and their value is shown. There are only a few simple rules for how expressions get evaluated that we'll address below. Feel free to experiment with entering expressions and seeing what gets returned.
The simplest expressions are just atoms, which are simple "indivisible" values, like symbols, numeric values, and strings. Some atoms evaluate to themselves just like in Ruby, so entering 12 into the REPL returns (and echoes) 12. "foo" works the same way. Symbols are a little different. Ruby uses a colon before the symbol's name, but in Scheme you refer to a symbol just using its name. So baz refers to the symbol with the name "baz". But if you enter baz into the REPL, Bus Scheme complains:
> baz
Error: Undefined symbol: baz
This is because symbols aren't considered literals; that is, they don't evaluate to themselves like they do in Ruby. When Bus Scheme encounters a symbol in this context, it treats it as a variable and tries to return the value that's bound to it, which doesn't work when it's not bound. So let's see what happens with a symbol that already has a value bound to it:
> +
#<Proc:0xb7c4b2a8@./bin/../lib/primitives.rb:16>
This
is the way Bus Scheme represents a built-in
(primitive) function. In Scheme, functions are first-class
values, so you can bind them to variables, like you can with
the lambda
keyword in Ruby. But in Scheme this the
primary way you refer to functions when you want to call them or pass
them to other functions.
Speaking of calling functions, it works something like this:
> (+ 3 4)
7
This is a list, which is Scheme's compound expression. This
list is made up of three elements, in this case all atoms: the
symbol +, the number 3, and the number 4. In normal
contexts, when Scheme sees a list it treats it as a function
call. First the first item in the list is evaluated, which evaluates
to a Ruby Proc object. Then each of the remaining list elements are
evaluated. Since they're all literals here, they evaluate to
themselves. Then the arguments get passed to the function. Behind the
scenes, this translates rougly into Proc.new{|*args|
args.sum}.call(3, 4)
. Let's see something a bit more
complicated:
> (+ (+ 1 2) (+ 3 4))
10
In this case, the first +
gets evaluated, and Bus
Scheme sees that it's a function. So it looks at its
arguments: (+ 1 2)
gets evaluated to 3, and (+ 3
4)
gets evaluated to 7. Then those two arguments get passed
to +
and the result becomes the value of the whole
expression.
That's the basics of how program execution happens, but you won't get far without having a few more functions under your belt. Here are a some to get you rolling:
+, -, *, and / You've been introduced to + above, but I'm sure you recognize your other old friends from grade-school days. + and * support any number of arguments, but - and / take two. In regular Scheme these all only work for numerical types, but Bus Scheme borrows Ruby's methods and lets you pass strings and other objects to + and *.
<, >, and =
These are comparison functions. They work like they do in any
language, but in Scheme you invoke them as (> 3 7)
etc. Again, Bus Scheme uses Ruby's underlying methods, so you can
pass strings and other objects in, unlike in regular Scheme.
list
If you want a list of numbers, you may think you get this by
entering (1 2 3)
. The problem with this is that in
normal contexts it gets treated like a function call, and it will
complain that 1 is not a function. What you can do instead
is (list 1 2 3)
, which evaluates to (1 2 3).
map
This works like Ruby's map, but it's a free-standing function
instead of a method. So instead of [1, 2, 3].map {|x| x +
3}
you would do (map (lambda (x) (+ x 3)) (list 1 2
3)
, which would return (4 5 6)
.
substring
Bus Scheme's (substring "foobar" 3 5)
translates
into "foobar"[3 .. 5]
in Ruby.
if
The most basic conditional is if
. Use it like
this: (if x "x is true" "x is false")
. if
evaluates its first argument, which in this case is x. If it
evaluates to a true value [3] then its second
argument gets evaluated and returned. If it's false then the
remaining arguments (if any) are evaluated and the last one is
returned.[4]
Well, that's enough for now. You may not know enough to be
dangerous, but I hope you know enough to explore. Tune in next time
when I uncover the true Secrets of Lisp™ by
explaining cons
, lambda
, and special
forms.
1 - I didn't say it was a large number.
2 - I imagine this causes Evan and Charles some varying amounts of distress.
3 - In Scheme every value is true except #f
, which is equivalent to Ruby's false
.
4 - Observant readers will note that this does not follow the evaluation rule for functions given above which states that every argument is evaluated before the function is called. This is because if
is not technically a function, but rather a special form, and different rules apply for the evaluation of a special form's arguments. There's more to this than I can cover in this article, but these rules allow for great syntactic flexibility.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK