mario.luis.guimaraes
2019-2-12 10:55:07

I have another beginners’ question: How do Racket macros differ from Common Lisp macros? Any links where I can find their difference described?


mario.luis.guimaraes
2019-2-12 10:57:31

I suppose Racket macros are more advanced, but would like to understand how more / what are the differences.


mario.luis.guimaraes
2019-2-12 10:57:32

Thanks


soegaard2
2019-2-12 10:59:01
  1. Common Lisp uses (bare) s-expressions to represent syntax. Racket uses syntax-objects.

soegaard2
2019-2-12 11:01:05

Syntax objects carry information on lexical context and source locations (line and column numbers) on each piece of syntax.


soegaard2
2019-2-12 11:02:58
  1. The “default” in Racket is to respect lexical/static scope - macro introduced variables does not shadow local variables.

soegaard2
2019-2-12 11:03:53

The property of 2. is called “referentially transparent”


soegaard2
2019-2-12 11:04:29

Actually I think I am confusing names.



mario.luis.guimaraes
2019-2-12 11:19:48

@soegaard2 Isn’t property two called hygiene ?


mario.luis.guimaraes
2019-2-12 11:26:39

My interest in Racket is as a tool for meta-programming on other platforms, like for the JVM or the Beam (Erlang). What do you think about this? Is Racket suited for this task? Is anyone using Racket like this? What is your opinion?


mario.luis.guimaraes
2019-2-12 11:28:22

So the idea is to use Racket for language-oriented programming on other industrial-strength platforms which do not have any or any good meta-programming functionality. Can Racket fulfill this task? Do you see any limitations?


mario.luis.guimaraes
2019-2-12 11:37:57

For example, I work on Elixir these days, which has a very similar macro system to that of Racket, but Elixir seems only for meta-programming on Erlang.


mario.luis.guimaraes
2019-2-12 11:38:30

I see Racket for meta-programming for anything. Is that so, or am I thinking too much about Racket?


soegaard2
2019-2-12 11:44:07

Standard Racket macros will eventually expand into “Core Racket” aka Fully Expanded Racket.


soegaard2
2019-2-12 11:45:01

The compiler/JIT then compiles to the current Racket backend.


soegaard2
2019-2-12 11:45:37

If you want to run the result on the JVM, you can’t reuse the Racket compiler/JIT.


soegaard2
2019-2-12 11:46:56

However what you describe sounds to me somewhat similar to what I tried to with Urlang.


soegaard2
2019-2-12 11:49:21

Urlang let’s you write JavaScript with Racket syntax. This allows the user to define new forms (macros) via the standard Racket macro machinery.


soegaard2
2019-2-12 11:50:09

As an example:


soegaard2
2019-2-12 11:50:14

(define-urlang-macro cond (λ (stx) (syntax-parse stx [(_cond [else e0:Expr e:Expr ...]) #'(begin e0 e ...)] [(_cond [e0 e1 e2 ...] clause ...) (syntax/loc stx (if e0 (begin e1 e2 ...) (cond clause ...)))] [(_cond) (raise-syntax-error 'cond "expected an else clause" stx)])))


soegaard2
2019-2-12 11:50:50

Here cond is being defined in terms of (JavaScript) begin and if.


soegaard2
2019-2-12 11:52:57

When the Urlang macro is defined, the identifier cond is associated with the above transformer.


soegaard2
2019-2-12 11:53:40

Later when the macro is applied, the “mark-transform-mark” method is used.


soegaard2
2019-2-12 11:53:49

(define (parse-macro-application ma) (debug (list 'parse-macro-application (syntax->datum ma))) (syntax-parse ma #:literal-sets (keywords) [ma:MacroApplication (define mark (make-syntax-introducer)) (let ((transform (attribute ma.transformer))) (mark (transform (mark #'ma))))]))


soegaard2
2019-2-12 11:58:17

@mario.luis.guimaraes If you want to know how Scheme macros can be implemented. A good start is: https://www.cs.indiana.edu/~dyb/pubs/bc-syntax-case.pdf


soegaard2
2019-2-12 11:58:34

“Syntactic Abstraction: The syntax-case expander” by R. Kent Dybvig


githree
2019-2-12 12:02:25

another example of source-to-source compilation on top of Racket: https://docs.racket-lang.org/magnolisp/index.html


mario.luis.guimaraes
2019-2-12 12:08:05

@soegaard2 @githree The idea is to have implement the target language’s “forms” directly in Racket, and then when a program created with only those forms gets to run it generates the equivalent program in the target language. Don’t no if Urlang or Magnolisp work like this, but basically this is my idea to use Racket for meta-programming on top of other platforms. The idea is also to map compile-time errors in the target language back to the same forms in the source written in Racket.


mario.luis.guimaraes
2019-2-12 12:09:36

So I do not need “Core Racket” or Racket’s compiler/JIT per se, other than to generate/interpret the Racket program that will generate the target platform’s program


mario.luis.guimaraes
2019-2-12 12:12:40

Then, having this translation base, I believe the rest of Racket’s macro/lang machinery could be used on top of this base to generate new langs / DSLs that would generate to this base, and then this base to the target platform. That is, the target platforms would be new “backends”, but not backends in the traditional sense for the “Core Racket”.


mario.luis.guimaraes
2019-2-12 12:12:43

Is this possible? Do you see any issues?


soegaard2
2019-2-12 12:16:56

I think the idea is sound.


soegaard2
2019-2-12 12:25:30

Not sure it is the simplest approach.


soegaard2
2019-2-12 12:25:46

No way to find out, but trying it.


mario.luis.guimaraes
2019-2-12 12:28:54

So is this different from what you did in Urlang? What is Urlang’s approach?


mario.luis.guimaraes
2019-2-12 12:30:12

Do you or others use Urlang for real world (i.e., non-trivial outside academia) projects?


mario.luis.guimaraes
2019-2-12 12:31:08

If yes, or not, what is / was your experience using Urlang in such projects?


mario.luis.guimaraes
2019-2-12 12:31:29

If you no longer use Urlang, why?


soegaard2
2019-2-12 12:32:00

I simply have a macro called urlang used like this: (urlang ..some JavaScript written with s-expressions..). The transformer thus gets the entire piece of JavaScript as a single syntax object. Then a traditional compiler (written using nanopass) compiles it to JavaScript.


soegaard2
2019-2-12 12:33:18

Next school year I am taking a sabbatical in order to write a math website. I intend to use Urlang for this.


mario.luis.guimaraes
2019-2-12 12:33:51

Ah, ok, it is clear now; that is a different approach then.


mario.luis.guimaraes
2019-2-12 12:34:43

Thanks for your responses :thumbsup:


mario.luis.guimaraes
2019-2-12 12:36:19

Is is there anyone from Racket’s core team that could add something to this discussion of using Racket to target other platforms?


soegaard2
2019-2-12 12:37:41

One issue with your approach: when a user writes a macro that produces form in your “target language in Racket” he needs to be careful, not to use “non-target-language” forms.


soegaard2
2019-2-12 12:38:05

Maybe you need to define your own version of define-syntax, that checks for this.


mario.luis.guimaraes
2019-2-12 12:40:05

Yep. But what if all the source files start with “#lang <target_platform>“? Does that issue still apply?


soegaard2
2019-2-12 12:40:23

Yes.


mario.luis.guimaraes
2019-2-12 12:41:13

I thought that defining “#lang <some-lang>” would automatically only accept only forms defined in some-lang … :thinking_face:


soegaard2
2019-2-12 12:42:42

Another issue: it will be difficult for you to perform any static analysis on compile time. If a macro inserts a variable reference to, say, foo and foo is unbound, then the problem will be detected by the compiler for the target language.


soegaard2
2019-2-12 12:44:04

About "#lang <some-lang>". I am assuming that you allow macros to be written in Racket.


soegaard2
2019-2-12 12:44:16

So you need to think about compile-time too.


mario.luis.guimaraes
2019-2-12 12:44:18

That is still compile time, but there are two compilation phases then: the one on Racket-side, the other on Target-side


mario.luis.guimaraes
2019-2-12 12:44:48

Yes the idea is to allow macros to be written in Racket


mario.luis.guimaraes
2019-2-12 12:46:33

The idea is for some-lang to “import” all of Racket stuff useful to define macros / other langs, on top of the base-lang forms


mario.luis.guimaraes
2019-2-12 12:47:59

So some-lang would be macros-lang + base-lang-for-target-platform


soegaard2
2019-2-12 12:48:34

Consider this example, the user wants to write: (define-syntax (target-cond stx) #'(target-if ...yada yada...)) but writes by accident: (define-syntax (target-cond stx) #'(if ...yada yada...))


mario.luis.guimaraes
2019-2-12 12:50:43

Pardon me, I may not understand some of Racket’s code, but I will try to figure your examples out ….


mario.luis.guimaraes
2019-2-12 12:53:34

so in this case I suppose you are saying that target-if is on the base-lang but the user types if which is well known from Racket, right? In that case if is not in the base-lang-for-target-platform and so is rejected by the definition of #lang base-lang-for-target-platform; or if is understood as a call to a target’s platform function that does not exist, and the compiler of the target’s platform will complain with an error, which gets mapped to the line where the form target-cond appears in the sources.


mario.luis.guimaraes
2019-2-12 12:53:49

Does this makes sense, or responds to your doubt?


soegaard2
2019-2-12 12:55:07

“In that case if is not in the base-lang-for-target-platform and so is rejected by the definition of #lang base-lang-for-target-platform;”


soegaard2
2019-2-12 12:56:10

The question is what scope if has where #'(if ...yada yada...) occurs.


soegaard2
2019-2-12 12:57:23

If if is in scope you will not see an “unbound identifier” error.


mario.luis.guimaraes
2019-2-12 12:59:33

Well the if is either a macro / function in scope the macros-lang at Racket-side, or it is a “target form” whose scope is defined by the semantics of the target platform. Does this makes sense? Perhaps I am not seeing where you are getting …


soegaard2
2019-2-12 13:02:40

Since the “target forms” are implemented as Racket macros - the target forms will be expanded into normal Racket code eventually.


soegaard2
2019-2-12 13:03:08

Therefore it will be difficult to check the output of a macro transformer.


soegaard2
2019-2-12 13:03:18

But maybe it is a non-issue.


soegaard2
2019-2-12 13:04:12

As long as the writer of a macro is aware that he must produce forms in the target language all is well.


mario.luis.guimaraes
2019-2-12 13:04:14

The point is anything that the Racket sources generate is a program that once run generates a program in the target platform. So the Racket’s program can only generate forms of the target platform, otherwise the target compiler will complain with its own errors, which the idea is to map back to the source Racket program where are the forms that generated the problematic target forms


mario.luis.guimaraes
2019-2-12 13:06:09

> As long as the writer of a macro is aware that he must produce forms in the target language all is well. Yep, for example if he or she tries to write forms that are not accepted in the target language, this is either caught by the definition of #lang some-lang or target-lang, or by the target compiler


mario.luis.guimaraes
2019-2-12 13:07:11

I would say my preference would be for those cases to get caught by the #lang some-lang or target-lang, but I am not aware yet if this can be done at Racket-side.


soegaard2
2019-2-12 13:09:24

It is the “get caught by the #lang some-lang” I see as a challenge.


mario.luis.guimaraes
2019-2-12 13:10:13

> Since the “target forms” are implemented as Racket macros - the target forms will be expanded into normal Racket code eventually. The idea is for this “Racket code eventually” to be the program that once run generates the target program


mario.luis.guimaraes
2019-2-12 13:11:07

For example, Elixir is all about meta-programming: in fact, people write a program that when interpreted generates another program


soegaard2
2019-2-12 13:11:22

Yes - and that means you can’t check whether the output of a macro transformer is “code that generates a target form” or “just some Racket code”.


mario.luis.guimaraes
2019-2-12 13:11:56

Every def... form in Elixir is a macro that generates calls to functions that created the modules, their functions, etc, all in bytecodes at the end


mario.luis.guimaraes
2019-2-12 13:14:02

Regarding > It is the “get caught by the #lang some-lang” I see as a challenge and > Yes - and that means you can’t check whether the output of a macro transformer is “code that generates a target form” or “just some Racket code”. I think (thought?) that the #lang LANG machinery in Racket only allows the programmer to use forms defined in LANG :thinking_face:


mario.luis.guimaraes
2019-2-12 13:15:03

and that would detect as errors the use of forms not defined in LANG (all this without ever reaching the target platform compiler)


mario.luis.guimaraes
2019-2-12 13:17:10

The other way around, that is, if a file starting with #lang LANG allows the use of forms outside of LANG, does not seem to make sense … or perhaps I am considering #lang to be capable of something it cannot …


soegaard2
2019-2-12 13:19:48

As a first approximation - yes. It holds for programs that doesn’t use macros.


mario.luis.guimaraes
2019-2-12 13:21:34

ok, I have to learn Racket’s macro and lang machinery to know about how feasible what I want to do is possible or not.


mario.luis.guimaraes
2019-2-12 13:22:14

A true strong language-oriented environment, in my view, should make this possible


mario.luis.guimaraes
2019-2-12 13:22:47

A true strong language-oriented environment, in my view, should make it possible to program to any target platform with ease.


mario.luis.guimaraes
2019-2-12 13:23:11

It seems I have to try doing this with Racket, and judge by myself.


d_run
2019-2-12 13:23:43

There are other attempts at compiling Racket to a different backend https://github.com/vishesh/racketscript


mario.luis.guimaraes
2019-2-12 13:23:48

Anyway, from what I have seem, Racket seems the closest around to that “true strong language-oriented environment”, that I am aware of


mario.luis.guimaraes
2019-2-12 13:24:44

@d_run My idea is not “compiling Racket to a different backend”, but to use Racket machinery to generate a target platform’s program



soegaard2
2019-2-12 13:24:55

Well, I don’t see how an algorithm can distinguish between “code in language S producing forms in language T when code is run” and “some code in language S”.


soegaard2
2019-2-12 13:26:57

But again - it might be a non-issue.


mario.luis.guimaraes
2019-2-12 13:30:01

@soegaard2 I don’t know if I got what you said … but having some Tool such that S -&gt; Tool -&gt; T is different from having a program of kind S -&gt; T. The Tool box there makes the difference, for example, mapping back errors from T to S, and forbidding the generation of unacceptable forms in T.


andreiformiga
2019-2-12 13:32:12

@mario.luis.guimaraes it is a different backend, even if the target language is a high level one


mario.luis.guimaraes
2019-2-12 13:32:20

Is there any thing out there better than Racket to create such Tool?


mario.luis.guimaraes
2019-2-12 13:33:14

@andreiformiga didn’t caught what you mean


mario.luis.guimaraes
2019-2-12 13:33:34

Note that the idea is not to translate any Racket program to some other platform, which is what a “new Racket backend” for me means


andreiformiga
2019-2-12 13:34:13

Then I misunderstood, sorry


mario.luis.guimaraes
2019-2-12 13:34:27

and which I believe is what Racketscript is trying to do @d_run


mario.luis.guimaraes
2019-2-12 13:35:21

The idea is to use Racket’s language-orietented machinery to generate programs of other platforms


mario.luis.guimaraes
2019-2-12 13:35:45

Say, write for the JVM but not in Java …


andreiformiga
2019-2-12 13:35:48

You said “a program that when run generates a program in the target platform”. To me, this is a compiler :grinning:


mario.luis.guimaraes
2019-2-12 13:36:00

write for the Erlang Beam, but not using Erlang


d_run
2019-2-12 13:36:20

isn’t that Elixir?


mario.luis.guimaraes
2019-2-12 13:37:53

@andreiformiga A compiler takes a source and processes it into another result. What I meant was to write a program, whose result is a program that once run produces a program in another platform. This is quite different


mario.luis.guimaraes
2019-2-12 13:38:59

@d_run Yes, I have said that previously, it is something much like what Elixir does for the Erlang VM. The idea is to do in Racket more or less similarly but for any other VM


mario.luis.guimaraes
2019-2-12 13:40:12

For example, I do not understand why there is LFE (Lisp-Flavored Erlang). LFE has its own compiler. Couldn’t LFE be done only using Racket’s #lang and macros machinery, instead as its author did, writing yet another parser and compiler?


andreiformiga
2019-2-12 13:42:22

What’s the input to your program that generates programs?


andreiformiga
2019-2-12 13:42:39

Elixir has a compiler


mario.luis.guimaraes
2019-2-12 13:46:01

@andreiformiga I have already said that before (this been a long thread, and you came later). But anyway, my thinking is having a #lang target-lang and to program in Racket for #lang target-lang, where the target-lang maps one-to-one the forms in the real target platform. I suggest anyone interested in this topic to read this thread from the beginning, if we want to have a more productive discussion.


mario.luis.guimaraes
2019-2-12 13:46:32

Racket seems the best tool for this job I am aware of, hence I came to this channel to try to understand if that is true or not.


andreiformiga
2019-2-12 13:47:17

Right, sorry


andreiformiga
2019-2-12 13:48:35

Still sounds like a compiler to me, but I’m probably missing something


mario.luis.guimaraes
2019-2-12 13:51:23

@andreiformiga there is no parsing involved, it is only generating target forms to be sent to the target compiler. So Tool (see above) is different from a typical compiler


greg
2019-2-12 13:57:22

As for your original #beginners question, @mario.luis.guimaraes you’d asked: https://racket.slack.com/archives/C09L257PY/p1549968907016000


greg
2019-2-12 13:57:34

greg
2019-2-12 13:59:02

That mainly explores one difference: Racket macros aren’t functions from s-expression to s-expressions. Instead they’re functions from “syntax objects” to “syntax objects”. Where syntax objects are sexprs + srcloc + lexical scope info.


greg
2019-2-12 13:59:47

There are other big differences, such as phases for deterministic separate compilation.


greg
2019-2-12 14:01:07

The blog post is a little dated (2011) — e.g. these days prefer syntax-parse to syntax-case — but it’s still maybe one helpful thing to read if coming from Common Lisp macros.


mario.luis.guimaraes
2019-2-12 14:02:18

Thanks for the useful links @greg


samth
2019-2-12 14:08:01

@mario.luis.guimaraes you might also be interested in https://dl.acm.org/citation.cfm?id=3005736


mario.luis.guimaraes
2019-2-12 14:10:01

@samth Is that link useful for the Racket vs CL macros initial question, or for the use case I am trying to achieve with Racket (the long previous thread mostly with @soegaard2) ?


samth
2019-2-12 14:10:10

the latter


mario.luis.guimaraes
2019-2-12 14:11:19

ok, I know you @samth are from the core Racket team. Do you think I can use Racket’s lang-oriented machinery to accomplish my “programming to other platforms in Racket” goal?


mario.luis.guimaraes
2019-2-12 14:11:47

Do you see any limitations / difficulties using Racket to do so?


samth
2019-2-12 14:12:55

I think that paper is a good start, but I don’t know how the details would work out for your use case


mario.luis.guimaraes
2019-2-12 14:14:40

ok, thanks


samth
2019-2-12 14:14:52

bkovitz
2019-2-13 01:31:18

How do you provide types from a module in typed/racket?


bkovitz
2019-2-13 01:53:08

Well, now provideing types seems to be working straightforwardly. Not sure what I was doing wrong before.