
Coding convention question: Should constants be capitalized, separated by dashes for long names?

The style guide doesn’t say anything about constants: https://docs.racket-lang.org/style/Textual_Matters.html#%28part._names%29

But there is a tradition to write constants in all caps:


@dave has joined the channel

Mellow greetings. I’m experimenting with a language for manipulating deeply nested data structures, where some validation has to be deferred to evaluation time. Is there an idiomatic way of hanging on to source locations that late, so that I can report more useful errors? Currently my only thought as a Racket newbie is to use a macro that expands to some kind of struct that stores both my s-expr and its corresponding syntax object, so that I can do parallel traversal of both and have “the syntax object that matches the expression I’m currently looking at” to hand.

but, not knowing everything that I don’t know, I have no clue if that’s a terrible idea :slightly_smiling_face:

The runtime state I’ll be dealing with is a bunch of s-exprs that layer on top of each other, where the eventual flattened result must conform to a couple different schemas. I want to be able to report useful errors that show the provenance of a bad piece of structure in the final flattened structure, pointing back to the source location where it got defined, if that makes sense.

Keeping the syntax object works. There are alternatives though. You can use a srcloc
struct or perhaps Eastlund’s package: https://docs.racket-lang.org/syntax/Source_Locations.html?q=source%20location

There are also correlated objects, which are like syntax objects but without the lexical context: https://docs.racket-lang.org/reference/linklets.html#%28def._%28%28lib._racket%2Flinklet..rkt%29._correlated~3f%29%29

Thanks both for the pointers! Sounds like there’s a bunch more reading in my afternoon :slightly_smiling_face:

is there a utility function somewhere to convert syntax objects to correlated objects? Seems like a straightforward “pull apart and reassemble” transformation, if I’m reading this correctly.

I don’t think there is, but it’s probably because correlated objects are fairly new and I think were really mostly just intended for the bootstrapping process at the linklet layer. But I don’t see any reason they couldn’t also be used for other things.

Got it. Maybe it’s a bad idea for me to be using linklet internals for my own nefarious purposes, but worst case it’ll give me something to imitate with my own structure.

How will you be using the source locations?

So, I’m using Racket to describe Terraform and Kubernetes objects, which are nested data structures that come with their own schemas. For instance, the schema might describe a shape like [(foo "bar") (baz (quux (zot 4)))]
, where baz.qux.zot
must be a positive integer and foo
can be anything. I want to be able to validate an expression against a schema, and be able to point to the precise source location of an error - something like baz.quux.zot must be a positive integer, but you gave it the value (not (a (number))) at my-config.rkt:45

Two complications are that I don’t always know the schema in advance, because e.g. Kubernetes lets you define custom types that you only discover when you go talk to a specific server; and the language I’m playing with lets you extend one expression with another, so you can override a deeply nested piece of expression A with expression B - and I want to properly track that, if B is the thing that doesn’t match the schema, I want to point there (and possibly also point out that it’s overriding A, which was defined in this other place)

I’m being a bit handwavy because, well, I’m still making it up :slightly_smiling_face: I’m very much in the experimentation phase.

I should add that I’m doing all this because, if I just flatten all the expressions extending each other into the final form and ship it off to the remote server, it’s going to give me various unhelpful errors. Best case, it’ll tell me that foo.bar.baz is 4, wanted a string
, but because of all the extending stuff, it might not be obvious where that 4
came from in the source. And worst case it’s just going to tell me “this object doesn’t match the schema, go away”, so I need to do the validation locally to get any useful information.

If you want DrRacket to color “my-config.rkt” red at 45 then you can use raise-syntax-error
which needs a syntax object that contains the source location (but the actual value of the syntax object is not important).

Ah, fantastic! I was hoping for an easy way to do that!

Would it possibly make sense simply to parse again from scratch once you have a schema? Parsing with no schema is more like a “lint”, could this work with some schema. And parsing again with a specific schema produces something usable for real. That would have the advantage you can just use the normal read/expand mechanism including syntax and error-reporting, as-is. That would have the disadvantage that parsing again would be slower.

Maybe that’s a dumb idea. OTOH I like to try the dumb ideas, first. ¯_(ツ)_/¯

But if that truly is dumb, the ideas above from other folks sound great!

hmm. I don’t understand how I’d get the builtin syntax and error reporting. Can you expand on that? I suspect I’m not picturing things the right way.

I guess more specifically, given some external schema, how do I end up with something where builtin read/expand can give me useful errors without writing my own parser anyway?

You’d still need to write a parser. I thought you meant, “When I parse, I can report syntax errors and stuff like that. But I can’t report things like schema errors, because I don’t have the schema, yet.” So my suggestion was just, don’t parse so soon.

Or, the user parse soon as a kind of “lint”, if you want.

ah, I see.

But just parse again when you have a specific schema.

And then you can report the errors using the usual syntax. That’s all

yup, got it. That does make sense! Honestly I’ve been doing parsing early just because that lets me experiment with the code structure without having to start dealing with external schemas just yet.

And I think there’s a small advantage to a “lint” pass, in that it’ll report some typo-ish errors faster and without having to go talk to slow servers for information.

Thanks!

You’re welcome. Again, if that ideas turns out to be bad, then the suggestions above were really good. I didn’t mean to detour you.

Not at all! This whole expedition is an excuse for learning, which might produce useful software as a side-effect. So this is exactly what I’m looking for!

My modest progress so far, fwiw. Now have a struct that captures the source locations of its child expression, so I can parse through it and raise useful syntax errors.

huh, I guess slack doesn’t know how to highlight racket. http://pasterack.org/pastes/31923 then.