
If I have types like these: (struct Foo ([x : Number]))
(struct Bar ([f : Foo]))
(struct SubFoo1 Foo ())
(struct SubFoo2 Foo ())
(struct SubFoo3 Foo ())
What would be a type definition that accepts Bar
only if Bar-f is of type SubFoo2?

I’m basically trying to translate some code from contracts to types

I mean I understand I could say (struct Bar (X) ([f : X]))
and then be able to say : (Bar SubFoo2)
or something similar, but I want Bar-f
to only be subtypes of Foo
and not just any type

@hoshom what you suggest there is exactly what I would do

@pocmatos related to your gccjit code, you might be interested in https://github.com/rjnw/sham which could presumably have a libgccjit backend

@samth thanks I took a peek at it last year and thought it was mostly a set of llvm bindings but I see it’s more than that. I like the idea of the sham language. And implementing a backend for that language using gccjit sounds definitely feasible? is rjnw one of your phd students?

Yes, he is

I also think you should see if the llvm jit that already works there can do what you need

@bkovitz I’m not an expert in this area, but I see similarities between SQL and map-reduce. SQL seems to have some odd restrictions on applying functions to sets of data. I couldn’t make a clean SQL implementation of the “square top 2” example unless I cheated by constraining the arguments to be non-negative. What struck me particularly was the use of take and sort (which also reminded me of APL).

thanks @samth i will take a more in-depth look at his code then. :slightly_smiling_face:

Do you prefer using shorthand style, like
(define (eat something)
(printf "~a is delicious!\n" something))
over the explicit λ style, like
(define eat
(lambda (something)
(printf "~a is delicious!\n" something)))
in Racket? I came from Scheme, been using the `explicit lambda’ style for a rather long time, but found the style guide https://docs.racket-lang.org/style/Choosing_the_Right_Construct.html?q=style seems to encourage the use of the former style.

Ubiquitous, unambiguous shorthand is usually better. Even Scheme wouldn’t have the former style at all if it intended you to write the latter.

Being taught to religiously write the latter is helpful to teach first-class functions, but it has few benefits for the working programmer.

@oldsin the first style, by far. Introduces less indentation and makes the declaration of a function and its arguments line up visually with uses of the function, like f(x)
and f(3)
.

The define/use symmetry is a good point! It’s always aesthetically pleasing when that sort of thing happens.

I am fond of similar symmetries in pattern-matching implementations.

@bill_temps (or anyone else reading), what I’m most wondering if the SQL-ish style is typical, “first-resort” pure-functional programming—and especially, if I’ve cultivated a good or bad habit by looking at things that way. I still wonder if it’s all that readable, and it bothers me that it tends to produce a lot of unnecessary dynamic memory allocation, especially lists that are quickly consumed.

@notjack @lexi.lambda Got it. Cheers!

@bkovitz I think that SQL-ish style is fairly typical, and I think it’s a good approach. The two main drawbacks I see to it are:
- The memory allocation problem, which you already mentioned. This can be fixed with better approaches to streaming data transformations that don’t require you to make a bunch of temporary collections (clojure’s transducers, java 8’s stream API, python’s generators / generator comprehension, haskell’s laziness, etc.)
- It’s often written “inside-out and backwards”, e.g.
(apply + (map square (take (sort xs >) 2)))
instead ofxs.sort(>).take(2).map(square).apply(+)
. That’s a purely notational problem though and can be fixed with things like threading macros:(~> xs (sort _ >) (take _ 2) (map square _) (apply + _))
.

@notjack Thanks—it helps a lot to get confirmation or correction from people with more experience. Indeed I liked the threading macros when I was programming in Clojure, for just that reason: they made a lot of code quite readable that otherwise would have been a mess. Hmm, that’s an interesting variation on the threading macro…

ah yeah I should clarify that I prefer to use something that isn’t technically what folks usually call the threading macro

I like to define (~> x f ...)
as a regular function that takes a single argument and a list of functions, and then I combine that with the fancy-app
package which lets you write anonymous functions using underscores

e.g. add1
is the same as (+ _ 1)

@notjack Say, when I’ve looked through Racket code (admittedly not a huge amount), I haven’t seen real-life use of a ~>
macro. Is there any particular reason why it isn’t common practice in the Racket world, beyond not already being standard? Are there other common ways to avoid the inside-out problem?

@bkovitz I think it’s not common practice only because it’s not in the standard libraries shipped with racket out of the box, so you have to find a package implementing it. And there’s multiple packages implementing it so you have to choose one. People underestimate how much effort that is.

Yep, it’s very easy to underestimate the paradox of choice.

especially when different people have different choices but need to share a codebase

like, I’m the only one I know who prefers the plain-function-plus-fancy-app approach

@notjack Say, you’re Jack Firth, aren’t you? I just watched your talk about expect
on YouTube a couple days ago, and I’m sold. Is there any way to use expect
with Typed Racket?

@bkovitz yup that’s me!

I think using expect
with TR might be doable with require/typed
, or it might require something more complicated. The tricky part is that expectations are parametric - there should be some way to say that expect/proc
has the type (-> (Expectation A) (-> A B) (Expectation B))
.

If you come up with something that works using require/typed
, feel free to send a PR to the expect repo that adds an expect/typed
module that just imports stuff using require/typed
and then exports it

I just recently moved about 1,000 lines of code to Typed Racket and found that it went pretty swimmingly and the result is better, clearer code that I can now modify with confidence. But I was disappointed to find that unit-testing doesn’t yet seem to work well with it. For example, check-equal?
in typed/rackunit
doesn’t report the line number of the failure. The error messages from expect
are fantastic—but, it looks like I’ll have to add some require/typed
code. OK, I’ll be more than happy to make that my first contribution to Racket, and no doubt I’ll learn some good stuff from it, too.

some of the rackunit-TR issues are known and have been partially worked on: https://github.com/racket/rackunit/issues

the rackunit codebase is a little hairy, but if you feel up to working on the unit-testing story in TR that might be a more effective place to contribute than expect
- a lot more people use plain rackunit
than expect

Ah, thanks, now skimming through the issues…

Thanks for the suggestion. Does the line number get lost in rackunit
because of the optimizations that TR does?

nah, it’s not even because of some TR-specific thing - it’s just that the typed versions of the check-equal?
/ check-pred
/ etc. macros don’t properly preserve source locations

I think @samth wrote a PR to fix this

yes, but it broke some stuff so wasn’t merged

OIC


@samth wait it did? :o

getting that to work generally would be nice

I think that’s why

OK, that looks like it will take some work. I’m balancing “getting something to work right now” vs. reading up on and learning more Racket. When I next get to the 2nd of those two phases, I’ll have a go at making a require/typed
wrapper around expect
; that might be easier.

@mbutterick my guess is that it’s the https redirect + 6.0 not supporting something in modern https

how many years old is 6.0 now?

Another question on style: as racket/list
provides first
second
third
, etc., to manipulate list, when do you use car
cdr
cadr
caddr
? Use them in a `mixed style’?

I use car
, cdr
, cadr
and try to avoid anything beyond that

@oldsin I’d recommend never using car
/ cdr
/ etc, if possible

they’re virtually unreadable

car
, cdr
, etc. are confusing for beginners, and me

first
, rest
, second
, etc. are a lot more readable

If you have a data structure built using pairs that doesn’t represent a list, then car and cdr makes sense.

true

Otherwise first, second, rest signals you are working on a list.

I think it’s a bad idea in general to make data structures out of raw pairs

I also prefer first
, rest
etc. I always have to think twice to remember what car
and cdr
represent (despite learning them decades ago)

They’re not unfamiliar to someone who’s used Lisp for a while. One advantage is that they can be combined, as in caddr
. Not very intuitive, no. Are there equivalent combining functions with first
, last
, et al.?

This thing really confuses me as, when you start as racket/base
instead of racket
, you do not own first
second
third
things.

Even once you’ve memorized them, they’re still no more descriptive names than random keyboard mashing

I wish there was a language in between racket/base
and racket
, that was just basically racket/base
+ racket/list
and racket/match
.

they become their own concepts, if you use them enough. but mostly i feel like they’re left over from a time before fast & easy structs

i’m still a racket beginner, so… why don’t you want to just use racket
?

it’s huge

as a relative newcomer, I do tend to use both and for the reasons above, when I am manipulating pairs I use car/cdr and when I am manipulating lists I use first/second etc. I find it does make the code more readable because it is more intentional.

which means slow load times and (more dangerously) higher chance of external libraries providing stuff that conflicts with the exports of racket

less of an issue for #lang racket
than for (require racket)
because required identifiers can shadow identifiers imported from a #lang

tangentially, what do you think of extending #lang reprovide
to make it easier to make whole languages that just modify the exports of other languages? I’ve wanted that on several occasions

curious as why this is an ssl error when the url is http just above

You mean a kind of “reprovide-as-language” thing?

If I wanted to define, say, #lang agile
as s-exp M
where M
provides everything from racket/base
, racket/list
, and racket/match
?

Here is a thing: if car
cadr
is really harder to understand, especially for newbies, then maybe first
second
are good alternatives. But what about cons
? I haven’t seen any alternate names of cons
.

I guess it would have to be constrained to “reprovide-as-s-exp-language”

@oldsin Honestly I’d prefer this:
pair
Constructor for pairs, e.g.(pair 1 2)
pair?
Predicate for pairspair-first
,pair-second
Accessors for pairs

Yes that

Okay. I could call it #lang reprovide/as-s-exp-language
, and have it generate the reprovides + a reader
submodule equal to… what? (module reader syntax/module-reader ???)

What do I put in the ???
hole?

Normally you would put a path to a module in a collection, but what if the current file isn’t installed within a collection?

Reminds me of typed/racket :racket-flat:

A relative path to the current module won’t work, because once someone uses it, that path will be relative to their usage file, not to the definition file…

Really learned from your ways. Thank you guys.

Would define-runtime-path
help?

I’d even just take racket/match
.

This doesn’t seem to work: (define-runtime-module-path self (submod "."))

Oh right, this isn’t possible because readers emit module syntax objects that directly say what their language is :(

The syntax/macro-lang
language deals with this problem by forcing the user to specify the path to the current file as an absolute collection path…

The reader of the wrapper language could be a wrapper function around the reader of the wrapped language. The wrapper reader could then postprocess the syntax object emitted by the wrapped reader and change the module’s declared language

Would that work?

If we were speaking I would tell you to say it again, slower this time.

The wrapper function does what? Takes a module syntax-object and returns a module syntax-object?

I’m not sure what I’m saying either

Takes a “read” function and returns a “read” function?

Wait, are you proposing a way that this could work with non-s-expression languages?

All langs have a reader function right? The thing that the reader submodule is supposed to provide? It accepts a port and some other arguments and returns a syntax object for a module, like (module #f implementation-language). Is that all mostly correct?

Yes. However, we can’t create a new implemnation-language on the fly

brb, getting laptop so I can write examples

Wait I just had an idea. It doesn’t work in the Racket module system but what if you could write something like: (module A (module B racket
(provide (all-from-out racket)))
(+ 1 2))

The language of the module A
is B
, and B
is defined on-the-fly

If we had that, I think we could do the reader wrapper that wraps a wrapped reader, or whatever you said

ah yes I caught up to your thought process and now see what you mean about how to specify the path to the current file

I think embedding modules in there would not be good because it ties together the expansion process of the two modules - we definitely want a weaker link between the two than that

Hey, does anyone have any thoughts about defining module-languages “on-the-fly” like this: (module A (module B racket
(provide (all-from-out racket)))
(+ 1 2))
Where the module A
has language B
, and the module B
has language racket
?

I’m imagining something like this: #lang reprovide
example/foo
example/bar
#:reader-from example/foo
…would be equivalent to this: #lang racket/base
(reprovide example/foo example/bar)
(module reader racket/base
(require (prefix-in base: (submod example/foo reader)))
(provide read-syntax)
(define (read-syntax ...)
(define mod-stx (base:read-syntax ...))
(replace-language-module-path-in-module-syntax mod-stx absolute/path/to/file/containing/reprovide/code)))

but I don’t know how to figure out that absolute/path/to/file/containing/reprovide/code
bit

and yes, I think this should work for non-s-expression languages

I would know exactly how this should work if module paths were just URIs. That would make everything here much simpler.

What if the example/foo
language produces multiple different module-languages in different situations?

you mean like metalanguages? like at-exp
?

We recently changed http to redirect to https, so that’s why the SSL error. Also, it looks like Racket before v6.1.1 used SLL 2/3 by default (which is now disabled and changed to TLS), which is probably why v6.0 fails.

As in sometimes #lang example/foo
produces (module name example/foo/1 ...)
, and other times #lang example/foo
produces (module name example/foo/2 ...)

That includes at-exp
, and I think it also includes pl
(a language that Eli Barzilay made for a class), and I could imagine a language similar in spirit to pollen
that did this too

And v6.0 didn’t have PLT_PKG_SSL_NO_VERIFY
, yet… so I don’t have a workaround at the moment.

It should be roughly equivalent to: (module B racket
(provide (all-from-out racket)))
(module A B
(+ 1 2))
Except that B
probably shouldn’t be visible outside.

couple of thoughts:
- maybe it would work if the user supplied a function to transform the source language module path instead of just overwriting it
- or maybe it’s enough to just support replacing a fixed finite set of languages
- when this pattern occurs outside metalanguages like
at-exp
, how is the choice of language usually made in practice? one of a few fixed possible languages? something more elaborate? I could seepollen
doing this to figure out a file path programmatically or something - does this use case even make sense for a
reprovide
-like thing? When would you be reproviding a metalanguage? I just think of reproviding as a way to bundle together a few simple modules to make another simple module - I’d never try to use reprovide to define a modified version of a metalanguage


I guess the difference between your thinking and my thinking is this: - You’re thinking of the module-language and the reader as more separate. You can extend the module-language without messing with the reader too much. - I’m thinking of the module-language dependent on the reader. The reader determines which module-language to use at read-time.

Right

In order to wrap a #lang
language to include an extra binding, you either need to: - determine the reader yourself separate from the #lang
that you’re wrapping, and then wrap the module-language - or, wrap only the reader, and not the module-language

Having (module _ (module _ racket stuff ...) stuff ...)
would allow you to wrap the module-language as part of wrapping the reader

It would, but other ideas would also work and I think it’s really not a good idea to treat modules as anonymous entities. It’s important that modules have names, and that those names are unambiguous and serializable. This property lets you separately read, expand, and compile modules.

Modules are definitely not lambdas

Lambdas allow expressions to “depend” on values that don’t come syntactically with them

Yes. So do links to named resources.

But the module-language-can-be-a-module-expression thing does not allow that

As in there’s something that would make lambdas hard, which does not apply to module-language-can-be-a-module-expression. module-lanugage-can-be-a-module-expression is not hard in the way lambdas would be

Keep in mind cacheability is a really important property to preserve here. In your example, the module expression containing the reprovides to perform must be copied and pasted into every single client module. That makes caching that work much harder (not impossible, but complex caching is often implemented wrong and its nearly impossible to test).

The module A depends on B, yes, but B is right there. The compiler can just compile B first.

Yes. But you don’t have just module A. You have hundreds of modules using this reproviding language. And each client module will get a copy of B pasted into it.

Yes, but that’s necessary if the module-languages produced by the reader are all different

which I don’t think happens all that often, and definitely wouldn’t in the use cases of #lang agile
and your request for a language that’s just racket/base
+ racket/list
+racket/match
.

I think languages like that would be 90% of the use cases

If I can’t do it for arbitrary readers producing arbitrary modules, then I’ll do it only for s-exp readers. Which is still 90% of the use cases

I’d do it for scribble/manual
pretty often

That’s a non-s-exp reader that doesn’t produce arbitrary modules (I think)

This change certainly affects v6.0 users who try to install packages from the default configured catalog, which is an even smaller group than v6.0 users generally. I’m not sure it’s reason enough to turn the global HTTP->HTTPS forwarding back off, but it’s a point in that direction.

@mflatt I’m trying to use free-identifier=?
to test if an id refers to another id from another file. It works perfectly before expand-syntax
, but after I expand-syntax
somehow the function returns #f
. Do you know a solution to this?

To demonstrate:
;; a.rkt
#lang racket
(define a 1)
a
;; compile.rkt
#lang racket
(define my-a #f)
(define my-b #f)
(define (annotate stx)
(let ([stx (expand-syntax stx)])
(println (syntax->datum stx))
(syntax-case stx ()
[(module _ _ (mb _ (define-vals (b) _) (app _ (lam () a) _)))
(set! my-a #'a)
(set! my-b #'b)]
[_ (void)])
stx))
(define orig (current-compile))
(current-compile
(lambda (e immediate-eval?)
(orig (annotate
(if (syntax? e)
e
(namespace-syntax-introduce
(datum->syntax #f e))))
immediate-eval?)))
(dynamic-require "a.rkt" #f)
(println my-a)
(println my-b)
(free-identifier=? my-a my-b)
This returns #t
.
But if now I have:
;; a.rkt
#lang racket
(require "b.rkt")
a
;; b.rkt
#lang racket
(provide a)
(define a 42)
;; compile.rkt
...
[(module _ _ (mb _ _ (define-vals (a) _)))
(set! my-b #'a)]
[(module _ _ (mb _ _ (app _ (lam () a) _)))
(set! my-a #'a)]
...
now it returns #f
.

We’ve turned off HTTP->HTTPS for now. Part of the idea is to check whether that fixes the package server, as you have suggested.

I also test the scenario where I have two submodules in a file, and that one works as expected. The only case that it doesn’t work is when they are in different files

From the module system’s perspective, those are not the same binding. The one in “b.rkt”’s expansion is “in the current module”. The one in “a.rkt”’s expansion is “from a module b.rkt relative to the current module”. There’s nothing that ties one module’s “current module” to the other module’s “b.rkt relative to the current module”. When you have submodules, there is something that ties them together, since they’re at once relative to the enclosing module.

For the multi-module case, you could use identifier-binding
and related tools to inspect binding information and impose rules about the relationship of “current modules”s, but I don’t know whether that’s the right direction for your purposes.

I see. Let me try identifier-binding
then. Thank you very much for your help :slightly_smiling_face:

This was a fantastic read, thank you

Is there a way to generate a module-path, like define-runtime-path
, that points to the current module?

I tried (define-runtime-module-path self (submod "."))

but that gave me an error about a cycle in the loading path

(variable-reference->module-path-index (#%variable-reference))
?

is this for the reader submodule reprovide problem?

@mflatt can a module path index go in the module-path part of a top-level module
expression? like (module foo <mpi> body ...)

No, it can’t

@mflatt what can? context: alex and I were discussing how to make something like #lang reprovide
that generates a read-syntax procedure that returns a module syntax object pointing to the reprovide module

Can the result of (require syntax/modresolve)
(resolve-module-path-index
(variable-reference->module-path-index
(#%variable-reference)))
go there?

In other words, does the following make sense, or sound completely wrong? #lang racket/base
(provide (all-from-out racket/base))
(require syntax/modresolve)
;(define-runtime-module-path self-module-path (submod "."))
(define self-module-path
(resolve-module-path-index
(variable-reference->module-path-index
(#%variable-reference))))
(module* self-module-path #f
(provide self-module-path))
(module* reader syntax/module-reader
#:language self-module-path
(require (submod ".." self-module-path)))

@sorawee I don’t know if it would be helpful — or even if it’s a good example to follow — but I’ve done some things with identifier-binding
to support visit-definition
in racket-mode. There are some edge cases like identifiers that are both contract-out
and rename-out
. AFAICT in such cases identifier-binding
can tell you the id came from a module, but not necessarily the original id. For that you have to walk the syntax. This may not matter for what you’re doing but just a heads-up. https://github.com/greghendershott/racket-mode/blob/master/racket/find.rkt

Thank you!!!

I’m not sure I follow, but maybe you want to use #:language
with a 5-argument procedure, where the second argument to the procedure is how the reader module was referenced. Then, you can build on that reference to access a module relative to the reader module.

Oh, so I could take (submod stuff stuff reader)
and extract just (submod stuff stuff)

Though I supposed one disadvantage of that is that someone could reprovide the read
and read-syntax
functions from a different module, and then that “how the reader module was referenced” argument might be different

I’ve finished making the s-exp
-only version on a branch here: https://github.com/AlexKnauth/reprovide-lang/tree/as-s-exp-language

That was probably the best explanation of why LOP is significant. Well done, sir!

@mbutterick Do you need v6.0 support, or would supporting/testing only v6.2 and later be ok?

Thank you for this link. Reading it now.