Live data from Hacker News

A Friendly Introduction to Racket

geometridae.bearblog.dev

121–130 of 195 posts

Re: A Friendly Introduction to Racket

#121
post #113
post #109

Earlier quoted context omitted.

I quite intentionally said "within the language" to preempt nonsense like "Of course, you can. C is perfectly capable of writing C interpreters and compilers."

What does 'within the language' mean? Is the standard library part of the language? Would a variant of C that came with an interpreter in the standard library (but no other changes) count as homoiconic?

No, that would not count as homoiconic. Homoiconicity means that a language natively supports its own syntax as a data structure. I used AI to help write an example of what C would look like if it were homoiconic. You should mainly look at the things that use `code{...}`. Note that the contents of the `code` value is precisely valid C syntax. It's not a string value that would require a C parser to operate on, and it's not a user-level tree using structs of pointers to operate on and different syntax to construct from what you actually write.

    #include 

    int main(void) {
 /* 1. Symbols: interned, identity-comparable. */
 symbol x  = #x;
 symbol x2 = intern("x");
 assert(x == x2);                       // same identity, not just strcmp
 assert(x != #y);
 printf("symbol name: %s\n", symbol_name(x));   // -> "x"

 /* 2. Code is written in *the same syntax* as code that runs.
  *    `code{ 1 + 2 }` is a value of type `code` whose printed
  *    form is literally "1 + 2".  No separate DSL. */
 code expr = code{ 1 + 2 };
 printf("as source: %s\n", code_to_string(expr));   // -> "1 + 2"
 printf("evaluates: %ld\n", (long)eval(expr));      // -> 3

 /* 3. Build the same AST programmatically; it is structurally
  *    equal to the literal above. */
 code built = code_add(code_int(1), code_int(2));
 assert(code_equal(expr, built));

 /* 4. Quasiquote: a code template with a hole, written in C syntax.
  *    `~n` splices the runtime value of n into the form. */
 long n = 40;
 code tpl = code{ ~n + (1 + 1) };
 printf("template:  %s\n", code_to_string(tpl));    // -> "40 + (1 + 1)"
 printf("evaluates: %ld\n", (long)eval(tpl));       // -> 42

 /* 5. A program can rewrite its own code, because code is data.
  *    Double every integer literal in a form. */
 code e2 = code{ (1 + 1) + 2 };
 code doubled = map_code(e2, double_int_literals);
 printf("rewritten: %s = %ld\n",
        code_to_string(doubled), (long)eval(doubled));
 // -> "(2 + 2) + 4 = 8"

 /* 6. Definitions are code too.  Write the definition in C syntax,
  *    then eval the code value to install it at runtime. */
 code square_def = code{
     long square(long x) { return x * x; }
 };
 eval(square_def);
 printf("square(7) = %ld\n", (long)eval(code{ square(7) }));  // -> 49

 /* 7. Macros: compile-time functions from code to code, written
  *    in the same syntax they transform. */
 code swap_macro = code{
     macro swap(a, b) {
  a = a ^ b;
  b = a ^ b;
  a = a ^ b;
     }
 };
 eval(swap_macro);                      // install the macro

 eval(code{ long u = 1; });
 eval(code{ long v = 2; });
 eval(code{ swap(u, v); });             // macro expands, then runs
 printf("after swap: u=%ld v=%ld\n",
        (long)eval(code{ u }), (long)eval(code{ v }));   // -> u=2 v=1

 return 0;
    }
Here is the code translated directly to S-expression syntax. This is not exactly conventional Lisp in terms of how variables are defined (explicitly typed instead of inferred is a bit weird for Lisp), but I wanted it to be as close as possible to see the parallels.

    (include )

    (defun int main ((void))
      ;; 1. Symbols: interned, identity-comparable.
      (symbol x  #x)
      (symbol x2 (intern "x"))
      (assert (== x x2))                          ; same identity, not just strcmp
      (assert (!= x #y))
      (printf "symbol name: %s\n" (symbol_name x))   ; -> "x"

      ;; 2. Code is written in the *same syntax* as code that runs.
      ;;    Now that the whole language is uniform, a plain quote is all
      ;;    it takes: '(+ 1 2) is a `code` value that prints back as
      ;;    "(+ 1 2)".
      (code expr '(+ 1 2))
      (printf "as source: %s\n" (code_to_string expr))   ; -> "(+ 1 2)"
      (printf "evaluates: %ld\n" (long (eval expr)))     ; -> 3

      ;; 3. Build the same AST programmatically; structurally equal.
      (code built (code_add (code_int 1) (code_int 2)))
      (assert (code_equal expr built))

      ;; 4. Quasiquote: a template with a hole.  ,n splices n's value.
      (long n 40)
      (code tpl `(+ ,n (+ 1 1)))
      (printf "template:  %s\n" (code_to_string tpl))    ; -> "(+ 40 (+ 1 1))"
      (printf "evaluates: %ld\n" (long (eval tpl)))      ; -> 42

      ;; 5. A program can rewrite its own code.  Double every int literal.
      (code e2 '(+ (+ 1 1) 2))
      (code doubled (map_code e2 double_int_literals))
      (printf "rewritten: %s = %ld\n"
       (code_to_string doubled) (long (eval doubled)))
      ; -> "(+ (+ 2 2) 4) = 8"

      ;; 6. Definitions are code too.
      (code square_def '(defun long square ((long x)) (* x x)))
      (eval square_def)
      (printf "square(7) = %ld\n" (long (eval '(square 7))))   ; -> 49

      ;; 7. Macros: compile-time code -> code, written in the same
      ;;    syntax they transform.
      (code swap_macro '(defmacro swap (a b)
     (set a (^ a b))
     (set b (^ a b))
     (set a (^ a b))))
      (eval swap_macro)                         ; install the macro

      (eval '(long u 1))
      (eval '(long v 2))
      (eval '(swap u v))                        ; macroexpands, then runs
      (printf "after swap: u=%ld v=%ld\n"
       (long (eval 'u)) (long (eval 'v)))   ; -> u=2 v=1

      (return 0))

Re: A Friendly Introduction to Racket

#122
post #2

any time the topic of racket comes up, i wonder if there are any interesting apps i could explore. but all i find is libraries and dev tools: https://awesome-racket.com/

For my own tools, some of them still in production at home and at work: https://github.com/DexterLagan?tab=repositories&language=rac...

Re: A Friendly Introduction to Racket

#123
post #38

Earlier quoted context omitted.

C has homoiconicity: you can represent C source code as C strings.

You can show this is possible, but it is extremely onerous in comparison.

Or not. Not even as a Turing Complete language.

Homoiconicity is a fundamental language property, not an algorithm nor an implementation issue. Lisp implemented in Pascal is still homoiconic. C is not homoiconic, whether the compiler is implemented in C or in Pascal, and C cannot be made homoiconic without adding fundamentally new and different constructs to the language definition.

Further sources:

https://wiki.c2.com/?HomoiconicLanguages

https://wiki.c2.com/?HomoiconicExampleInManyProgrammingLangu...

Re: A Friendly Introduction to Racket

#124

Earlier quoted context omitted.

And then mini/microKanren put Prolog in its rightful place: that of a useful DSL instead of a poor general purpose language.

miniKanren is a replacement for a Prolog like a shitty tree-walking sexpr interpreter that doesn't even have modules is a replacement for Racket. I don't even know what to say to you for suggesting that, frankly. Anyways the embedded inference engine as an idea failed a long time ago, Americans convinced themselves it was the way to do things and just refused to ever let it go. It's just extra baggage on the importan…

Instead of us two milling about: https://minikanren.org/minikanren-and-prolog.html (and this is both informed and pretty impartial from what I can read)

Re: A Friendly Introduction to Racket

#125
post #44

Earlier quoted context omitted.

It looks more like a "program" for an HP15c pocket scientific calculator. Not necessarily a bad thing but also not exactly what I would call "expressive". You may be able to build anything you want --- which may be efficient when communicating with the machine. But I wonder about now well this would communicate with other programmers.

It's not a program, it is (obviously) a totally unrealistic hodgepodge expression for a list of constant values that simply demonstrates the literal syntax for a variety of types of values (real numbers, dotted pairs, rational numbers, complex numbers, polar coordinates, etc. -- Racket is unusually expressive in the types of literals). Your comment demonstrates something typical of HN, and it's not a good thing. It's…

First time dealing with developers I see. I’m sorry.

Re: A Friendly Introduction to Racket

#126

On a more personal note, for some weird reason, Racket ended up being the language that got me one of my most important contracts. Through a whole series of butterfly-effect events, that eventually led me into CAD software development, which is where I discovered my love for metamaterials. It’s funny how these things happen!

because autocad is scripted in lisp ?

Re: A Friendly Introduction to Racket

#127

> no special syntax for anything. (list '(1. . #\#) -5/6+7.s-8i `(1 ,@2) 1@1 ;hmmm, no unquote splicing comma ;-) 10# ;surprised? (list #i+1 +1i 1+i) ;complicated or complex? #e-1e10i ;Old MacDonald? "(* 9 10)" #())

How did it manage the feat of being more baroque than CL? Why that #e madness instead of https://www.lispworks.com/documentation/HyperSpec/Body/f_rat... ?

Re: A Friendly Introduction to Racket

#128
post #14

Earlier quoted context omitted.

it allows you to create a language that compiles to your original language. You can abstract everything away. Not like Haskell where laziness accounts for some and typeclasses for some (and often an exponential growth in compile times). No, it property let's you change the language. I got tired of loops sucking and made this, for example: https://rikspucko.koketteriet.se/bjoli/goof-loop

This doesn't strike me as better than the alternatives. In Haskell, it's just a few different functions to handle the different use cases. That strikes me as far better than one "function" that magically does different things depending on how it's called. It is local, I'll give you that. But it still seems bad because you have overloaded the semantics along multiple axes simultaneously. I prefer building blocks with…

everybody agrees, which is why most sane people very rarely reach for macros. At the same time, it would be great if some of the libraries that rely on typeclass machinery for code generation could use lisp-like macros, because turning a 3 second recompile to a 35 second recompile (and I have experienced worse. Much worse.) is what kills exploratory programming. I heard optics mostly solves much of the problems of generic lens, but god forbid you use something like servant or polysemy.

Chez compiles a project of about 35000 lines (with heavy macro usage) in less than 0.5s on -O2.

Re: A Friendly Introduction to Racket

#129

> no special syntax for anything. (list '(1. . #\#) -5/6+7.s-8i `(1 ,@2) 1@1 ;hmmm, no unquote splicing comma ;-) 10# ;surprised? (list #i+1 +1i 1+i) ;complicated or complex? #e-1e10i ;Old MacDonald? "(* 9 10)" #())

It looks more like a "program" for an HP15c pocket scientific calculator. Not necessarily a bad thing but also not exactly what I would call "expressive". You may be able to build anything you want --- which may be efficient when communicating with the machine. But I wonder about now well this would communicate with other programmers.

I actually wrote an audio sequencer in HP RPL as a kid.

It was far less complex to code, and human readable. Unicon was pretty cool also, but never became popular. Erlang/Elixir will probably become more relevant as people move into massively parallel constraint solvers.

The monolith centric languages are simply no longer appropriate for many classes of problems. =3

Re: A Friendly Introduction to Racket

#130

> no special syntax for anything. (list '(1. . #\#) -5/6+7.s-8i `(1 ,@2) 1@1 ;hmmm, no unquote splicing comma ;-) 10# ;surprised? (list #i+1 +1i 1+i) ;complicated or complex? #e-1e10i ;Old MacDonald? "(* 9 10)" #())

Curious, how would look that in Python?

Would need some imports:

    import cmath
    from fractions import Fraction
And then have to write numbers as strings to input them. From their examples:

    >>> Fraction('1.414213 \t\n')
    Fraction(1414213, 1000000)

    >>> cmath.sqrt(-2-0j)
    -1.4142135623730951j
Not really great, but workable. I don't like having to import things for basic math stuff like exact numbers, but having a lot of special syntax is maybe also not that great. I am guessing except for maybe macros, there is no other way, if one wants to take input (code) literally as exact numbers in all cases. If one were to just write down a number like `1.414213562373`, and expect it to be exactly represented in the program, then it cannot be read as a floating point number first and then converted into an exact number internally, because reading it as floating point number could already lose precision. So this kind of logic to interpret exact numbers would have to live somewhere before that. So it is in the reader. I am not sure how one could employ a macro instead, and then maybe access the digits of a written number, or whether that is possible. I think it should be possible. If it is, then of course things could be made into macros, after which one could use something like:

    (exact 1.4142135623730951)
[1]: https://docs.python.org/3/library/cmath.html [2]: https://docs.python.org/3/library/fractions.html
Post reply on HN