leafac
2018-4-6 11:40:55

Thank you :grinning:


racket588
2018-4-6 14:13:47

@racket588 has joined the channel


racket588
2018-4-6 14:18:41

Hello. I have some questions about writing my own language(s).


racket588
2018-4-6 14:19:05

I have created a “core” language that is basically just an s-exp language.


racket588
2018-4-6 14:19:52

I have created another “dsl” language which I want to expand to the core language, and have that language process further.


racket588
2018-4-6 14:20:07

So, in other words, I want to chain the languages together like passes in a compiler.


racket588
2018-4-6 14:20:25

I am not really understanding how to do this, though.


racket588
2018-4-6 14:21:25

racket588
2018-4-6 14:22:23

When I try the above, it says all of my macros are unbound. I suspect it’s because the macro expansion takes place in the context of the module some-mod which will, therefore, use “core” which is ignorant of my macros.


racket588
2018-4-6 14:23:07

So, (if that’s correct), what is the proper way to indicate I want to expand all of the “dsl” macros, then treat the result as a module in the “core” language?


racket588
2018-4-6 14:23:20

If that assumption is wrong, then what in the world am I doing wrong?


pocmatos
2018-4-6 15:50:26

@alama what’s the best way to report typos, layout issues with Server Racket?


lexi.lambda
2018-4-6 16:04:55

@racket588 I don’t think I fully understand your question, but I think you would normally just make forms in your DSL language macros that expand into forms in your core language, then let the expander handle the desugaring.


racket588
2018-4-6 16:05:41

Imagine it like this: I type…


racket588
2018-4-6 16:05:57

x = 3 and that parses to…


racket588
2018-4-6 16:06:53

(define-new-var x 3) which I want to then use macros to expand to…


racket588
2018-4-6 16:07:15

(define-var x 3) which is valid in the core language, but should expand to…


racket588
2018-4-6 16:07:27

(define x 3) so that it executes in racket


racket588
2018-4-6 16:07:49

I have a language extension that gets the last two steps, which is my “core” language


racket588
2018-4-6 16:08:19

I have another language extension that does the first two steps, which is my “dsl” defined in main.rkt


racket588
2018-4-6 16:09:14

Once I expand from (define-new-var x 3) to (define-var x 3), my main language will no longer expand and I get errors because define-var is not defined in Racket


racket588
2018-4-6 16:09:51

So, the crux of the problem is how do I do what you said so that the core language macros will be used to further expand the language?


racket588
2018-4-6 16:11:16

And for context, I want to be able to have multiple backends which can a) output and compute racket, b) generate equivalent java, c) output analysis information about the code. Also, I want to have multiple DSLs that can be used in the beginning.


racket588
2018-4-6 16:11:28

Which is why I am trying to do this in separate stages.


racket588
2018-4-6 16:11:57

I know that selecting the frontend and backend is another problem, but just chaining them together would be a big step forward at this point.


racket588
2018-4-6 16:12:12

@lexi.lambda I hope that makes more sense…


lexi.lambda
2018-4-6 16:17:49

I think you would want to require the module that provides your core module forms where you define your dsl language forms so that they hygienically reference the core forms and the identifiers aren’t unbound.


racket588
2018-4-6 16:18:25

I think I tried that, but I can try again.


racket588
2018-4-6 16:19:11

You mean put the require into the template for the dsl macro, right? In other words, it should be in the module that I output from my dsl?


lexi.lambda
2018-4-6 16:19:45

No, the require should be in the module that defines the DSL macro, not inside the macro’s template.


racket588
2018-4-6 16:19:57

ah, ok


lexi.lambda
2018-4-6 16:20:01

Are you familiar with racket’s hygiene model?


racket588
2018-4-6 16:20:07

vaguely


lexi.lambda
2018-4-6 16:20:39

To a first approximation, the idea is that scope should “just work”, even for macros.


lexi.lambda
2018-4-6 16:21:16

So if you use an identifier in a macro template, it should be in scope where you define the macro, and whether it’s in scope where the macro is used is irrelevant.


racket588
2018-4-6 16:22:58

Ok, that got it. Thanks!


racket588
2018-4-6 16:23:58

I guess I am just thinking of it in a different manner. Instead of just making all the macros available, I was looking at it as more of a pipleine. Take input, get an AST, compile to the core language, then compile to racket.


racket588
2018-4-6 16:24:45

I think I need to think about the next problem a bit now (swapping out backends and frontends) to make sure I can do it this way, but this will at least help me get ahead with my prototype. Thank you very much! @lexi.lambda


lexi.lambda
2018-4-6 16:25:16

Swapping out backends is significantly harder, unfortunately.


racket588
2018-4-6 16:25:37

That’s what I’m thinking…


lexi.lambda
2018-4-6 16:26:03

It can be done, but it involves a bit more trickery.


racket588
2018-4-6 16:26:05

I could have my dsls expand to a module containing my macros, but with the require parameterized, though, right?


racket588
2018-4-6 16:26:46

So, my actual dsls would be templated themselves


racket588
2018-4-6 16:27:00

and a macro would plug in the proper required lib


racket588
2018-4-6 16:27:27

(I’ll worry about that when I need to, haha)


lexi.lambda
2018-4-6 18:42:58

@alexknauth I think I remember why I rejected the solution of just sticking a type in a syntax property… sometimes the type is not self-contained. For example, imagine I have the type (Foo x), where x is intended to be a type variable bound by some forall. I want to arrange for the type to be under a forall [x] so that the final piece of syntax is (forall [x] (Foo x)). Now, when this expands, forall will introduce fresh bound variables to end up with something like (let-syntax ([x (type-var-transformer (type:bound-var #'x1))]) (Foo x)), and the whole thing eventually becomes (type:forall #'x1 (type:app (type:con #'Foo) (type:bound-var #'x1))).


lexi.lambda
2018-4-6 18:44:44

But this doesn’t work if I don’t have (Foo x) as syntax, but instead only as a prefab structure, since it means I actually have something like (type:app (type:con #'Foo) (type:bound-var #'x)). So if I stick that on a syntax property, I’ll end up with the let-syntax wrapped around a piece of syntax with that syntax property… and the resulting type I’ll get is (type:forall #'x1 (type:app (type:con #'Foo) (type:bound-var #'x))) (note the final #'x instead of #'x1).


lexi.lambda
2018-4-6 18:47:19

I really do need that use of #'x to be expanded, but the prefab structures are already fully expanded by definition. The problem here is that I essentially want to embed an unexpanded type, in this case the identifier x, inside of a type that has already been expanded, in this case (type:app (type:con #'Foo) _), where _ represents a hole to be filled in by my macro.


lexi.lambda
2018-4-6 19:06:31

But wait, there’s more: the type my macro receives is actually (type:app (type:con #'Foo) (type:bound-var #'a1)), and my macro essentially wishes to transform that into #'(#t(type:con #'Foo) x), where #t represents the embedding of a prefab type into syntax by attaching a syntax property. But it doesn’t know what shape the input type will be, only that it needs to substitute all bound #'a1 variables with x. So while I could theoretically provide a way to convert prefab types into syntax objects, including a protocol that could properly encode all the various different types that can show up in the prefab structures, that would end up producing something like (#%app (#%type:con Foo) (#%type:bound-var a1)), and the macro author would then need to reimplement bound variable substitution on syntax objects (since the typechecker’s inst function only works on prefab structures).


lexi.lambda
2018-4-6 19:09:31

So, as far as I can tell, this dualism in the type representation becomes a problem as soon as you want to embed an unparsed/unexpanded type inside of a parsed/expanded one. It would be possible to create some hacks around this issue, like allowing an escape hatch in the prefab type language that allows for “3D types” (which permit arbitrary values in the type language in the same way 3D syntax allows arbitrary values in the syntax language), then a type->syntax function that uses syntax values inside 3D types directly… but that’s a lot of hacks to preserve the prefab structure representation.


lexi.lambda
2018-4-6 19:11:47

Now, to be fair, I currently only have one use case for this technique of embedding an unexpanded type inside an expanded one (the use case is typeclass deriving), but I get a lot of value out of the prefab representation everywhere else. So maybe it will turn out that those tradeoffs are worth it. It’s hard to know without actually doing any real programming in Hackett.


lexi.lambda
2018-4-6 19:12:38

(@ben, you might be interested in the above as well.)