
Show HN: G-Fu – a pragmatic Lisp embedded in Go - codr7
https://github.com/codr7/g-fu/tree/master/v1
======
codr7
Author here.

It's still a young language and naive implementation focused on correctness,
but I'm very happy with how its turning out.

I'm especially fond of first class environments and qualified identifiers [0];
which allow composing, overriding and delegating data and behavior in a
convenient yet remarkably flexible way. It's clearly a more fundamental
mechanism than the usual suspects.

Note that this is the opposite of what Python is doing with __dict__ and Lua
with tables. Rather than basing the implementation on a general purpose data
structure, g-fu exposes the the implementation and allows hooking into it.

It reminds me of pcostanza's work on Context Oriented Programming and ContextL
[1], but integrated rather than bolted on.

[0]
[https://github.com/codr7/g-fu/tree/master/v1#environments](https://github.com/codr7/g-fu/tree/master/v1#environments)

[1]
[https://github.com/pcostanza/contextl](https://github.com/pcostanza/contextl)

~~~
nudq
> first class environments

Which are notorious for being unoptimizably slow

> It reminds me of pcostanza's work on Context Oriented Programming and
> ContextL

I'm not reminded at all. Can you expound?

~~~
codr7
How so? I get that going the route of Python and Lua and using a general
purpose data structure for the environment isn't optimal. But g-fu exposes the
already existing environment as a separate type, the only thing it knows how
to do is map symbols to values and access is restricted to a few special forms
just like any regular Lisp enviroment.

Well, context oriented programming is all about separating the system into
layers of behavior that may be flexibly and conveniently composed to get the
right mix of functionality. Which is exactly what g-fu is doing with first
class environments.

------
lincpa
Clojure in go:
[https://github.com/candid82/joker](https://github.com/candid82/joker)

~~~
codr7
I find Clojure refreshing, but it has too many agendas for me and Java is just
(a) no Go. I tried, I did. And launching the repl felt like watching grass
grow on my latest/greatest macbook. And if I'm going to use a Lisp in Go, I
definitely prefer one designed around Go to one imported from Java.

The Clojure-code I've written, while terse; always feels too far off from what
I want to say. I have the same issues with Haskell, too much hoop jumping to
take part in someone else's quest for purity.

There's plenty I like in there and Rich is a very disciplined thinker. The
languages have much in common. g-fu's way of using environments looks a lot
like Clojures use of maps if you squint.

Transducers are brilliant, it's difficult for me to believe it took this long.
Which is why g-fu uses [0] them as the foundation for all sequence
transformations.

[0]
[https://github.com/codr7/g-fu/blob/master/v1/lib/iter.gf](https://github.com/codr7/g-fu/blob/master/v1/lib/iter.gf)

------
aasasd
BTW, there's a Lisp that compiles to Go AST, and then derives Go code from
that. (Won't find it right now, though.)

~~~
codr7
I've been considering compiling the interpreter as a shortcut to native
executables, but to Go rather than its AST since that allows reusing the
existing runtime.

The main idea behind G-Fu is to complement Go by providing a more expressive
experience while delegating the heavy-lifting. Which means that performance is
far from the top priority.

~~~
aasasd
There's an approach to compilation where you take the input program, but
instead of running it you output what the interpreter would do, as a new
program. So you get meh performance but you have an executable.

Here's a project doing that:
[https://news.ycombinator.com/item?id=19508616](https://news.ycombinator.com/item?id=19508616)

This is a theory around this approach:
[https://en.wikipedia.org/wiki/Partial_evaluation#Futamura_pr...](https://en.wikipedia.org/wiki/Partial_evaluation#Futamura_projections)

~~~
codr7
Yep yep, we're talking about the same thing. I like to call it an inline
interpreter.

It's a nice compromise. The emit-logic can be kept fairly uniform and low-
maintenance since its more or less identical to the regular interpreter.

I got around 10% increased performance on a Forth in C for whatever that's
worth.

One thing to keep in mind is to use functions in the emitted code rather than
simply dumping everything in a pile. Most compilers don't deal well with code
piles from my experience , which makes the code both compile and run slower
than it could.

------
chias
This looks really cool :)

Though full disclosure, I clicked for the jokes about Greenspun's tenth rule,
and I'm vaguely disappointed I didn't find any.

~~~
codr7
I'd guess an experimental Lisp interpreter embedded in Go is just too obvious
:)

But providing Gophers with a slightly more polished alternative that still
composes well with Go-code might help avoid the worst nightmares.

------
jstewartmobile
How do you call out to Go libraries with this?

~~~
codr7
You would have to wrap the call in a g-fu prim [0] for now. As for dynamically
loading libraries, the plan is to implement g-fu libraries that call out as
plugins. I have yet to dig into reflection to see how much effort an FFI would
take.

[0]
[https://github.com/codr7/g-fu/blob/master/v1/src/gfu/abc.go](https://github.com/codr7/g-fu/blob/master/v1/src/gfu/abc.go)

