Live data from Hacker News

Hy 1.0 – Lisp dialect for Python

github.com

121–130 of 131 posts

Re: Hy 1.0 – Lisp dialect for Python

#121

Yay! The birth of a language is a beautiful thing. I’m curious about the macros: how are these implemented? They seem like pretty straightforward unhygienic Lisp macros, which is a little bit of a disappointment, but better some macros than none at all! Anything about the macro system that distinguishes it from the Common Lisp system? E.g. anything borrowed from Scheme or Racket? Docs are sparse here.

Sparse? I got a whole chapter for ya: https://hylang.org/hy/doc/v1.0.0/macros

Yes, and it's a very nice tutorial! I'm interested in implementation details. Maybe there's no hygiene (and no scope sets etc.) to worry about—that would probably make documentation a little shorter. I'm sure the documentation will grow as people run into edge cases.

(I'm also probably a little spoiled with documentation coming from Racket which has like 4 big chapters dedicated to different aspects of macros scattered around the docs, plus some associated papers. Forgive me—I'm not trying to dunk on Hy; I just like reading docs.)

Re: Hy 1.0 – Lisp dialect for Python

#122
post #111

Earlier quoted context omitted.

There's nothing wrong with CL macros. Quite the opposite, they are more powerful than the alternatives. Macros are power tools, dumbing them down for safety is missing the point.

I have a hard time trusting the CL macros I write because of unexpected interactions with the context that I use them in. While it is the case that CL macros are more powerful than the R6RS macros-by-example system, Racket’s system (and some other newer languages that have adopted things pioneered by Scheme and Racket, such as Elixir) give you hygienic macros without sacrificing expressive power. I want my macros to…

They are not unexpected though.

If you put your own symbols into generated code you better have a damn good idea what you're doing.

It's a power tool.

Re: Hy 1.0 – Lisp dialect for Python

#123

Earlier quoted context omitted.

Sparse? I got a whole chapter for ya: https://hylang.org/hy/doc/v1.0.0/macros

Yes, and it's a very nice tutorial! I'm interested in implementation details . Maybe there's no hygiene (and no scope sets etc.) to worry about—that would probably make documentation a little shorter. I'm sure the documentation will grow as people run into edge cases. (I'm also probably a little spoiled with documentation coming from Racket which has like 4 big chapters dedicated to different aspects of macros scatte…

Admittedly, I've tried not to document the implementation. Yeah, they're pretty much simple dirty Common Lisp macros. Internally, they're functions that are called with the arguments converted to models (via `hy.as-model`), and then the return value is converted to a model. If a macro's first parameter is named `_hy_compiler`, it gets access to the current compiler object; this is undocumented since it's only meant for internal use. Reader macros have no parameters, but can access the current reader object as `&reader`. When it's defined, a reader macro is added to the current reader's dispatch table.

Re: Hy 1.0 – Lisp dialect for Python

#124
post #122

Earlier quoted context omitted.

I have a hard time trusting the CL macros I write because of unexpected interactions with the context that I use them in. While it is the case that CL macros are more powerful than the R6RS macros-by-example system, Racket’s system (and some other newer languages that have adopted things pioneered by Scheme and Racket, such as Elixir) give you hygienic macros without sacrificing expressive power. I want my macros to…

They are not unexpected though. If you put your own symbols into generated code you better have a damn good idea what you're doing. It's a power tool.

Table saws were only improved by the addition of an emergency stop to prevent people from maiming themselves. Power tools don’t have to be dangerous.

If you are wanting to introduce variable capture you better be really explicit about when you want it.

If there’s no hygiene, I have to know everything about how the macro is implemented in order to trust it and use it confidently. Might be fine for small shorthand, but that won’t scale. You need non-leaky abstractions to build on them.

Racket’s `syntax-parse` and “syntax parameters” show that you can have it both ways: procedural macros that are hygienic by default, but with an explicit escape hatch when you do want to introduce new bindings into the macro call site. It also gives you much much better errors.

CL macros are about as dangerous as malloc/free, but without years of experience and tools like Valgrind to debug. They’re hard to trust and get right.

Racket macros are like GC/affine typing: everything is correct by construction.

Re: Hy 1.0 – Lisp dialect for Python

#125
post #31

Earlier quoted context omitted.

not a standalone distribution but: uvx hy@1.0.0 gets you into the Hy REPL echo '(print "hi hn")' > hi.hy uvx hy@1.0.0 hi.hy prints "hi hn" https://docs.astral.sh/uv/guides/tools/#running-tools (context: uv can install and manage python versions)

Generally, uv answers the objection that ‘Python sux’ in that it (1) is correct, unlike pip, and (2) is freaky fast.

Except uv doesn’t support conda so there goes many of the niche scientific packages required for many users like me. Someone please prove me wrong because I do love uv when I can use it. I’ve found pixi to be an ok alternative but not nearly as fast.

Re: Hy 1.0 – Lisp dialect for Python

#127
post #122

Earlier quoted context omitted.

They are not unexpected though. If you put your own symbols into generated code you better have a damn good idea what you're doing. It's a power tool.

Table saws were only improved by the addition of an emergency stop to prevent people from maiming themselves. Power tools don’t have to be dangerous. If you are wanting to introduce variable capture you better be really explicit about when you want it. If there’s no hygiene, I have to know everything about how the macro is implemented in order to trust it and use it confidently. Might be fine for small shorthand, but…

I've used table saws, with and without protection. They're all dangerous as fck, because removing the chance of getting hurt means converting it to a completely different kind of tool. Chain saws, same thing. Power tools.

Of course we want them to be as safe as possible, but that's a different discussion. All attempts I've seen so far have dropped functionality to get there.

Re: Hy 1.0 – Lisp dialect for Python

#128
post #127

Earlier quoted context omitted.

Table saws were only improved by the addition of an emergency stop to prevent people from maiming themselves. Power tools don’t have to be dangerous. If you are wanting to introduce variable capture you better be really explicit about when you want it. If there’s no hygiene, I have to know everything about how the macro is implemented in order to trust it and use it confidently. Might be fine for small shorthand, but…

I've used table saws, with and without protection. They're all dangerous as fck, because removing the chance of getting hurt means converting it to a completely different kind of tool. Chain saws, same thing. Power tools. Of course we want them to be as safe as possible, but that's a different discussion. All attempts I've seen so far have dropped functionality to get there.

> All attempts I've seen so far have dropped functionality to get there.

Well, then I recommend you take a look at Racket's macro system: Racket gives you hygienic macros without any loss of power. (It's actually more powerful and expressive than CL macros.)

Re: Hy 1.0 – Lisp dialect for Python

#129
post #127

Earlier quoted context omitted.

I've used table saws, with and without protection. They're all dangerous as fck, because removing the chance of getting hurt means converting it to a completely different kind of tool. Chain saws, same thing. Power tools. Of course we want them to be as safe as possible, but that's a different discussion. All attempts I've seen so far have dropped functionality to get there.

> All attempts I've seen so far have dropped functionality to get there. Well, then I recommend you take a look at Racket's macro system: Racket gives you hygienic macros without any loss of power. (It's actually more powerful and expressive than CL macros.)

So they allow you to introduce arbitrary symbols in expansions? Doesn't that mean we're back to regular CL macros?

Re: Hy 1.0 – Lisp dialect for Python

#130
post #129

Earlier quoted context omitted.

> All attempts I've seen so far have dropped functionality to get there. Well, then I recommend you take a look at Racket's macro system: Racket gives you hygienic macros without any loss of power. (It's actually more powerful and expressive than CL macros.)

So they allow you to introduce arbitrary symbols in expansions? Doesn't that mean we're back to regular CL macros?

The way you phrased that suggests you're only familiar with CL-style macros, where arguments to macros are nested lists of symbols a function or variable is known by it's name (a symbol) and nothing more.

Racket's model is much more sophisticated and powerful. The input to a macro in Racket is a syntax object [1], which combines the CL-like quoted expression with additional source and lexical binding information. This means that in Racket, unlike CL, a variable is not just it's name—it's also all this other information. Racket uses scope sets to track binding information in a sane and hygienic manner across different macros and functions.

So, if you want to introduce an identifier that the macro caller can interact with (note: I said an identifier—you can introduce any symbols you want but they'll be different identifiers because their scope sets will be different) you need to explicitly state that you would like to create an identifier with a particular scope set. [4]

But that's the old, dumpy, clunky way of doing things. Thanks to recent research, we have much better ways of introducing identifiers in a sane, hygienic way. Gregg Hendershott's excellent "Fear of Macros" walks through making the `aif` macro using syntax parameters [3] which let you cleanly introduce new bindings. (See the paper "Keeping it Clean with Syntax Parameters" he's linked to in his post.)

So, in short, no, we're not back to regular CL macros because Racket prevents us from accidental variable capture but gives us an easy way to do 99.999% of the use cases for breaking hygiene (syntax parameters) and then one more way (`datum->syntax`) just in case we really need to do something out of the ordinary. In either way, Racket lets you express your intent with macros better and more precisely than CL.

[1]: Racket syntax objects: https://docs.racket-lang.org/guide/stx-obj.html

[2]: Syntax model, scope sets: https://docs.racket-lang.org/reference/syntax-model.html

[3]: "Fear of Macros", writing the `aif` macro: https://www.greghendershott.com/fear-of-macros/Syntax_parame...

[4]: Reddit thread on making an unhygienic macro: https://www.reddit.com/r/Racket/comments/tpgaa4/is_there_a_w...

Post reply on HN