
Perchance to Scheme (2016) - tosh
https://hardmath123.github.io/perchance-to-scheme.html
======
baldfat
Racket is not academic to me. Racket equals programming fun and really is a
joy to use. Academic seems to not convey the feeling one gets when using
Racket. To me Racket is really the future of what languages will look like, a
language to create your own DSL.

The greatest strength of Racket is its documentation. Secondly the way Racket
actually teaches you how to be a better programmer. I personally grew the most
due to "How to Design Programs"
[https://htdp.org/2018-01-06/Book/](https://htdp.org/2018-01-06/Book/) Third
the executables for cross platform is its reason why I use Racket at work. I
can make it and create an executable with raco.

I feel like Racket is the most general purpose language I have ever used. I
grew up on C64 Assembly, Pascal and Forth. When I learned Python I felt like I
could do anything. That was until I wanted my Python code to be used on a
computer that wasn't my own computer.

Python was my go to language for everything at work but I ended up moving to R
about 6 years ago. I felt my R was rough around the edges and picked up Racket
to learn about R's Scheme roots. It was a night and day difference. I have
several pet programs that saves me hours every week.

My Racket programs are much more readible than my Python Scripts. The
libraries are certainly not all there but it is actually easy and fun to
create your own implementations in Racket and a library for your own use.

~~~
JadeNB
> Racket equals programming fun and really is a joy to use. Academic seems to
> not convey the feeling one gets when using Racket.

As an academic, it makes me very sad to hear these qualities, that I feel
describe my work, used as the _opposite_ of 'academic'.

~~~
baldfat
I was faculty at a University and I still find the word Academic to be
negative connotation and was an inaccurate statement on Racket

Define - Academic

1\. c: very learned but inexperienced in practical matters 1\. d: based on
formal study especially at an institution of higher learning 3\. a:
theoretical, speculative 3\. b: having no practical or useful significance

[https://www.merriam-webster.com/dictionary/academic](https://www.merriam-
webster.com/dictionary/academic)

Couldn't find a positive way to spin Academic as used by the article.

~~~
WindowsFon4life
Academic as in, there are many places where the person[s] working on the code
or feature abruptly stopped. As if they graduated and were no longer working
on it.

~~~
baldfat
That certainly isn't the case with Racket either :)

------
drmeister
Alternatively - you can choose to program in Common Lisp - an industrial
strength language that has been demonstrated in large projects like the QPX
flight search engine (Google Flights).

For anyone who doesn't know - a big difference that Common Lisp brings to the
table is that it has separate namespaces for variables and functions. This may
not sound like much but it makes it much easier to write macros (code that
writes code) and facilitates compile-time computing.

Another big difference between Scheme and Common Lisp is that the Common Lisp
specification is _much_ larger and it comes with a much larger collection of
standard built-in functions and macros and a standard object-oriented
programming facility, among many other things.

I know this because I've spent the last six years implementing Clasp
(github.com/clasp-developers/clasp) - a Common Lisp that interoperates with
C++ and uses LLVM as the backend. There are several excellent implementations
of Common Lisp - Steel Bank Common Lisp is a great place to start.

~~~
madhadron
> a big difference that Common Lisp brings to the table is that it has
> separate namespaces for variables and functions. This may not sound like
> much but it makes it much easier to write macros (code that writes code) and
> facilitates compile-time computing.

A little more detail here: it has a lot more namespaces than that. Indeed, you
can attach arbitrary different namespaces of things to symbols if you want to.

It turns out that when you're writing macros in Lisp, the majority of what you
use from outside the macro are functions and the majority of symbols that you
bind and use within the macro are variables, so if you put them in different
namespaces, you get rid of the majority of cases where symbols don't do what
you were expecting in a macro.

Now, it's not quite all of the cases, and when it does happen, it's a pain to
track down. Which is why the Scheme folks spent the effort and complexity on
hygienic macros (where the system rewrites all symbols internally to make
variable capture impossible). At the time of Common Lisp's creation, multiple
namespaces and unhygienic macros were the obvious choice for production code,
since there weren't hygienic systems available. Today you can go either way.
In terms of research, hygienic is the route to go, as the work over the last
couple of years on strongly typed, hygienic systems has shown.

~~~
kbp
> A little more detail here: it has a lot more namespaces than that.

And I don't think the function/value split is even the most important
namespacing it does with regards to macro hygiene ("functions are less likely
to be shadowed" always struck me as a kind of weak argument). Symbols being
namespaced under packages is a much more robust solution:

    
    
        CL-USER> (defmacro m (x) `(car ,x))
        M
        CL-USER> (defpackage :p)
        #<PACKAGE "P">
        CL-USER> (in-package :p)
        #<COMMON-LISP:PACKAGE "P">
        P> (cl:macroexpand '(cl-user::m x))
        (COMMON-LISP:CAR X)
    

CL:CAR isn't accessible in P, so (car x) by itself would signal UNDEFINED-
FUNCTION, but the macro works correctly. Defining a P::CAR function wouldn't
change anything.

That isn't just for functions, either, eg if you do:

    
    
        (defmacro awhen (pred &body body)
          `(let ((it ,pred))
             (when it ,@body)))
    

(where you _want_ to leak IT), and then import AWHEN but not IT, you'll get an
error that YOUR-PACKAGE::IT isn't bound to anything when you try to use it; it
doesn't matter that AWHEN-PACKAGE:IT is bound.

I don't disagree with anything you said, I just think it made CL macros look
flimsier than they are.

~~~
kazinator
The problem is that it is quite common for _cl:car_ to be accessible in
packages, because of the practice of use-ing the cl package everywhere.

Even if I'm in my own package, I can't do this:

    
    
      (flet ((car (x) ...)))
        (m arg))
    

because in order to have a hassle-free Lisp coding experience in my package, I
brought in all the public symbols from the common-lisp package. So my _flet_
is in fact shadowing _cl:car_.

Even if I just import the specific common-lisp things I need, and _car_ is not
one of them, that could change. Today, that _car_ above is _mypkg:car_.
Tomorrow, someone edits the _defpackage_ to import _car_ (because they needed
it in a function they added), and now when that file is re-read, _car_ is
_cl:car_.

Packages have a theoretical solution to the hygiene problem that will not be
air-tight in practice due to use/importation.

Another problem is that programmers aren't going to define a large number of
packages to protects parts of their program from each other. A common practice
is just to make one package for an entire project.

You need fine-grained package use to achieve near perfect hygiene. Ideally,
each module that provides macros should have its own package, and use only
symbols from that package in the macro expansion. If module A uses a macro
from package B, and that macro generates a local function F, that will be
B::F, not interfering with the A::F function. If the modules are in the same
master project package, the clash is not averted, obviously.

------
canuseeme
> Clojurescript.... Personally, I don’t like it on a matter of principle: I
> don’t like things that compile to JavaScript. It doesn’t seem like anything
> you really need to write better code.

Maybe I don't understand what the alternative is when you want to write client
side code for the browser - js?? I use Clojurescript and find clear benefits.

By way of example I often found the general setTimeout functionality to be a
nightmare for scheduling delays. Most of the time it works, but periodically I
find it blocks or fails (via a dependancy chain) and then have to spend time
arranging code abnormally to mitigate. In clojurescipt I use core async.

    
    
      (defn timeout [ms]
        (let [c (async/chan)]
          (js/setTimeout (fn [] (async/close! c)) ms)
         c))
    

Never looked back. Never had a delay problem - Ever.

cljs core.async and many other features have made it so much more enjoyable to
write client side code. Don't even get me started on callbacks.

> Oh, and one last thing. I feel it’s obligatory at this point for me to say,
> please don’t spend too much time researching Scheme dialects. Just pick
> Racket and start coding

I've had the opposite experience so thanks, but no thanks.

~~~
all2
What was your experience with Racket?

~~~
canuseeme
This dates back about 5 years ago, but I found Racket's libraries were rough.
Often they amounted to prototype work that never got fleshed out, they were
ill maintained, low in quality, and many just wouldn't load. And these were
general purpose libraries; like even just basic database stuff.

I moved on to Clojure.

------
erjiang
I am guilty of writing my own Scheme (R5RS) interpreter as well, and a key
takeaway from the experience was that many things calling themselves Scheme
interpreters are missing core parts of Scheme.

Implementing a language that resembles Scheme is quite easy. Implementing
call-with-current-continuation and hygienic macros AND getting the tricky
little details correct is not.[0] An analogy might be someone offering
"ability to write SQL" for their fly-by-night database without stating up
front that joins are not supported.

[0] Old test suite run against old Schemes: [http://sisc-
scheme.org/r5rs_pitfall.php](http://sisc-scheme.org/r5rs_pitfall.php)

~~~
plinkplonk
"Implementing a language that resembles Scheme is quite easy. Implementing
call-with-current-continuation and hygienic macros AND getting the tricky
little details correct is not."

(tangential) and this is where most "build your own compiler" style books fall
down.

They teach you to implement ultra simple languages, but don't tackle memory
management/garbage collection, concurrency, production grade typecheckers, and
for lisplikes, macro systems, etc.

~~~
nils-m-holm
> (tangential) and this is where most "build your own compiler" style books
> fall down.

Have a look at [http://t3x.org/s9book](http://t3x.org/s9book) \- it covers a
Scheme implementation including GC, call/cc, low-level macros, type checking,
tail call elimination, etc.

------
gus_massa
A minor update: Now Chez Scheme is open source (Apache license)
[https://github.com/cisco/chezscheme](https://github.com/cisco/chezscheme)

~~~
GeoffKnauth
Racket is now integrating the best parts of Chez Scheme, for a future release.

~~~
dTal
Racket-on-Chez will be considerably slower than vanilla Chez - Racket
semantics incur some overhead. Here are some rough benchmarks from a year ago:

[http://blog.racket-lang.org/2018/01/racket-on-chez-
status.ht...](http://blog.racket-lang.org/2018/01/racket-on-chez-
status.html#%28part._benchmarks%29)

------
i_feel_great
Other schemes that may be of use:

1) Gambit. Compiles to C. Interesting tools built on top are Termite (Erlang-
like concurrency framework) and Lambdanative

2) TinyScheme - super minimalist embeddable scheme.

3) Kawa Scheme - on JVM. Much smaller and faster than Clojure when I used it
last.

4) Bigloo - can compile to C & Java bytecode.

Note ChezScheme is now open source and has very good documentation.

~~~
snaky
To add to the list, PicoBit Scheme for microcontrollers, currently PIC18 and
ARM,
[https://github.com/stamourv/picobit](https://github.com/stamourv/picobit)

There was a port of one of PicoBit predecessors, Bit Scheme, to the XMOS
architecture, with realtime GC and support for the native XMOS features
[http://soft.vub.ac.be/~cderoove/publications/master_thesis_r...](http://soft.vub.ac.be/~cderoove/publications/master_thesis_ruben_vandamme.pdf)

------
bjoli
As a guile user myself, I feel I must say that guile works just fine also when
you are not embedding it. It is a very capable scheme, and even though it is
not as fast as chez or racket, things have gotten a lot better (the upcoming
JIT in the 3.0 release means at least a 2x speedup).

~~~
bjoli
Oh,and thanks to pthreads and guile fibers the concurrency situation is
probably the best of any scheme.

------
linuxlizard
Does the sheer amount of cool stuff out there feel overwhelming to anyone
else? I want to learn this and this and this and that and this and that thing
and this other thing over there.

~~~
gnulinux
Because of this I can't focus on anything because learning anything feels like
it has infinite opportunity cost, spirals into anxiety. My irrational response
is to watch Netflix because if I were instead learning X, I would be concerned
that I'm not learning Y0, ..., YN for a very large N.

------
phoe-krk
> ClojureScript is a compiler from Clojure to JavaScript (Clojure originally
> targeted Java). It is not a Scheme dialect, not does it feel like one. It’s
> a LISP dialect.

Hell no. The linked website,
[http://wiki.c2.com/?LispSchemeDifferences](http://wiki.c2.com/?LispSchemeDifferences)
lists differences between _Common_ Lisp and Scheme. Clojure is not a Common
Lisp implementation.

~~~
gcornut
Clojure(Script) is neither a Scheme nor an implementation of Common Lisp but
is a LISP dialect. LISP dialect languages are simply a group of lisp-like
language not just implementations of Common Lisp.
[https://en.m.wikipedia.org/wiki/List_of_Lisp-
family_programm...](https://en.m.wikipedia.org/wiki/List_of_Lisp-
family_programming_languages)

~~~
lispm
Scheme is generally also a LISP Dialect, more so like Clojure, since it
inherits many of the LISP features directly.

Generally Clojure has very little in common with LISP dialects, besides some
concepts. It has no linked lists, almost no functions are the same, almost no
macros are the same, no special forms are the same, similar named operators
often do something different, ... Think ATOM, CONS, CAR, CDR, APPEND, SETQ,
NULL, DOLIST, DOTIMES, ... basically the original core of LISP is not present
in Clojure. Literally no Clojure code runs in LISP dialects and no LISP code
runs in Clojure dialects -> without completely rewriting it. Some Scheme ran
in LISP - with more or less effort to do so. Nowadays that's not very common.

Clojure may in abstract terms be a LISP dialect, but the expectation that
anything from LISP can be applied directly to Clojure is not the case. You
can't take any LISP book and execute the examples in Clojure , nor would it
make much sense.

------
rgrau
There's this new implementation called "gerbil"[1] which runs on top of
gambit, is still very young but looks very promising.

It's opinionated in a good way (IMHO).

[1]. [https://cons.io/](https://cons.io/)

~~~
JasonFruit
Looking at it, I'd call it a new language, not a Scheme implementation. Its
semantics are much different from Scheme's.

~~~
WindowsFon4life
Umm superficially you could say it is anything. The reality is it IS Scheme.
(define (foo bar) (format "hi ~a" bar)) works just fine. As will any of the
Scheme code, as it's merely syntactic sugar, (import :std/sugar) that you are
confusing with a whole new language.

~~~
JasonFruit
Look at things like:

    
    
        (define lst '(4 5 6))
        (set! (car lst) 3)
        (display (car lst))
    
        ;; 3
    

That's a pretty serious semantic departure. It's at least as different from
RnRS as Racket, which insists it is not Scheme.

~~~
kbp
There's an SRFI for generalised set! that lots of implementations (Racket,
Guile, Chicken, etc) support:
[https://srfi.schemers.org/srfi-17/srfi-17.html](https://srfi.schemers.org/srfi-17/srfi-17.html)

~~~
JasonFruit
Neat. Learn something new every day.

~~~
soegaard
It's not in widespread use though.

------
sdevlin
Emacs Lisp is most certainly _not_ a Scheme.

~~~
teddyh
The plan is to replace Emacs Lisp with Guile, which _is_ a Scheme:

[https://www.emacswiki.org/emacs/GuileEmacs](https://www.emacswiki.org/emacs/GuileEmacs)

~~~
lispm
No, that's not the plan. The plan is to replace the Emacs Lisp runtime with a
Guile-based runtime.

See here: 'is a branch of GNU Emacs that replaces Emacs’s own EmacsLisp engine
with the Elisp compiler of Guile.'

The word is 'ENGINE'. That's the underlying language runtime.

It's neither GNU Emacs right now, nor will Emacs Lisp be replaced.

~~~
kamaal
I thought that project is perennially under development, and at this point in
time more like good thing to look at than having a real chance of getting
merged with master?

~~~
lispm
Probably. ;-)

------
stcredzero
_Scheme, on the other hand, is very loosely specified. This is a good thing:
the core language is so small that the entire standard fits on about 50
pages...It’s not nebulous, it’s simply minimalist. This is why Scheme is more
of an idea than a language._

Smalltalk wound up being quite loosely specified. This, combined with the
small "core," enabling single developers rolling their own, resulted in a high
degree of fragmentation of the overall language community.

~~~
soegaard
I noticed that quote as well.

Odd thing to call RnRS Scheme loosely specified. The RnRS spec is extremely
well-written - and it even includes a semantics.

Now "Scheme" used to mean "a Scheme like language" is of course loosely
specified - but that's another story.

------
funkaster
One important thing the author failed to mention (probably due to the
publication date) is that Guile also has compiler front ends for ECMAScript
and Emacs lisp (and there's a lua front end in the works). So far, Guile is my
go-to scheme.

[https://www.gnu.org/software/guile/](https://www.gnu.org/software/guile/)

~~~
WindowsFon4life
And Gambit has backends for JS, Java, Ruby, Php, C and no doubt some others.

------
nerdponx
I know this is a big controversial can of worms, but as a filthy casual who
just happens to like Lisp, the idea of R6RS is pretty appealing to me. This
post seems very much R5RS-oriented.

Are there any R6RS-conformant Schemes that are recommended?

~~~
kkylin
Not a R6RS fan myself, so no definitive answer. But AFAIK Chez (now open
source as someone else posted) is R6RS-compliant and one of the fastest
implementations. Also, R7RS-small is final I think. Not sure about R7RS-large.
Here's an old discsussion:
[https://news.ycombinator.com/item?id=11700167](https://news.ycombinator.com/item?id=11700167).

~~~
ballpark
I think Chicken Scheme recently became r7rs in their new version 5

~~~
Globz
Chicken R7RS is still a work in progress AFAIK

------
threatofrain
The author concludes with:

> Oh, and one last thing. I feel it’s obligatory at this point for me to say,
> please don’t spend too much time researching Scheme dialects. Just pick
> Racket and start coding.

~~~
merlincorey
I prefer Chicken Scheme, which recently just had a new release (version 5).

It actually compiles to C and then to binaries for your specific platform.

Check it out: [https://www.call-cc.org/](https://www.call-cc.org/)

~~~
nerdponx
The fact that Chicken actually has a package manager and central repository is
a big plus for me.

~~~
GeoffKnauth
Racket also has a package manager and central repository.

~~~
WindowsFon4life
Gerbil has a package manager too.

------
WindowsFon4life
These days Gambit/Gerbil are a much more pragmatic Scheme.

------
jacobush
What about PicoLisp?!

------
nikofeyn
what about mit/gnu scheme?

