laurent.orseau
2021-3-22 08:44:04

adding the headers '("Cache-Control: no-store,max-age=0") to get-pure-port doesn’t do anything, and the response I obtain is still the old document, even though my browser displays the new one


laurent.orseau
2021-3-22 08:45:29

(This is the <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#preventing_caching|recommended way by w3schools>)


laurent.orseau
2021-3-22 08:48:07

I’m trying to make a video for RacketFest and this is somewhat blocking :sob:


laurent.orseau
2021-3-22 08:54:32

The issue still happens if I open a new racket console


kellysmith12.21
2021-3-22 08:56:33

In addition to the top level being hopeless, I’ve learned that phase separation can be frustrating.


laurent.orseau
2021-3-22 09:07:37

The returned header contains Cache-Control: max-age=300 despite the header I sent. Am I using the headers properly?


laurent.orseau
2021-3-22 09:33:47

(sorry for the flood @soegaard2, I’m just putting all the info in the same place) Even if I use all the same headers as Chrome it still returns the old document: '("accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" "accept-encoding: gzip, deflate, br" "accept-language: en-GB,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6" "cache-control: no-cache" "pragma: no-cache" "referer: <https://gist.github.com/>" "sec-ch-ua: \"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"" "sec-ch-ua-mobile: ?0" "sec-fetch-dest: document" "sec-fetch-mode: navigate" "sec-fetch-site: cross-site" "sec-fetch-user: ?1" "upgrade-insecure-requests: 1" "user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36")


laurent.orseau
2021-3-22 09:44:53

The answer contains cache hits: X-Cache: HIT X-Cache-Hits: 1 It’s like the no-cache headers are not effective from Racket.


ryanc
2021-3-22 09:50:46

Right. The answer to your original question is no. You can think of a variable (or other binding) as being named by a pair of an identifier and a phase. A definition like (define fun ....) at phase n defines a variable identified by (fun, n). You can (but generally shouldn’t) use syntax-shift-phase-level to create a reference to (fun, n+1), but you would still need to tell Racket what to do to create that variable. There’s no way to just copy the value from (fun, n), because at phase n+1 the variable (generally) doesn’t have a value yet; it won’t actually get a value until phase n is run. The point of the module system is to keep bindings in sync with what definitions must be executed to support them—in other words, “You Want It When?


kellysmith12.21
2021-3-22 09:52:50

That does pose a serious problem to dependent types…


popa.bogdanp
2021-3-22 11:27:58

Cache-control is a response header so setting it on a request will have no effect. Racket itself won’t cache anything on its end so either the upstream server is caching based on some kind of heuristic or you might not be hitting the endpoint you think you are (in particular this could happen if you’ve recently changed DNS entries — IIRC Chrome has its own DNS resolver, whereas Racket will use the system resolver).

What do you get if you run the same request using curl -v?


popa.bogdanp
2021-3-22 11:31:34

Are you using Fastly by any chance?


laurent.orseau
2021-3-22 11:37:03

From what I read, it seemed to me that cache-control is both for requests and responses (e.g., cache-control: no-cache in HTTP 1.1 appears to be replacing pragma: no-cache from HTTP 1.0). I’m not using fastly that I know of.


laurent.orseau
2021-3-22 11:39:47

Indeed curl -v returns the same thing. DNS resolving then, hmm


ryanc
2021-3-22 11:40:32

It limits your choice of language mappings. If you are implementing a language in Racket, then you need to decide how to map the concepts of your language onto Racket. Some language mappings won’t satisfy your requirements. For example, if you were implementing #lang java, representing Java classes as Racket variables bound to Racket classes would be insufficient to implement Java’s scoping rules and to detect various kinds of errors at compile time. You would need to consider a language mapping where a Java class name is mapped to Racket static info that points to the run-time support (similar to what Racket’s struct form does). Common sources for constraints are scoping rules, errors that must be detected at compile time, interoperation rules with other modules (with the same lang, with Racket, and with others). The conclusion I would draw is that a dependently-typed language cannot just map its variable definition form to Racket’s define. It must do something to make that definition available to the (compile-time) type checker.


popa.bogdanp
2021-3-22 11:42:15

If flushing the DNS cache on your machine doesn’t fix it (it might not if your ISP is the one doing the caching), the fastest way to test this out might be to add an entry to /etc/hosts for the domain pointed at the right IP address.


popa.bogdanp
2021-3-22 11:42:37

Or make the call to the IP address directly and pass a Host header whose value is the domain name (so that virtual mapping will work (assuming a reverse proxy))


kellysmith12.21
2021-3-22 11:44:03

A naïve solution would be to map each definition in the source language to two identical Racket definitions, one at compile time, and one at run time. Although, that seems rather heavy-handed. That’d only work for a restricted form of dependent types.


laurent.orseau
2021-3-22 12:56:34

Thanks for your help @popa.bogdanp. purging the dns cache didn’t work (sudo systemd-resolve --flush-caches on Ubuntu), and passing the ip with host: didn’t work either :confused: I haven’t tried the /etc/host option yet


greg
2021-3-22 14:07:59

@laurent.orseau A couple ideas: - What if you open a “private browsing” window in Chrome, and try to visit the URL? Maybe some cookie for auth or whatever is causing you to get the newer version, else not. - Back in non-private mode, maybe enable “developer tools” and look at the “network” tab to confirm what the requests and responses really are, in the case where you do see the newer verison.


greg
2021-3-22 14:08:47

Also if it’s still not working, and you don’t mind sharing aurl, maybe some of us could try it and see what we discover.


laurent.orseau
2021-3-22 14:12:24

{dev tools} That’s what I did to obtain the exact GET request that chrome was doing so as to mimic it (including user-agent). No change. The url is at http://gist.github.com\|gist.github.com. Sharing mine won’t help, since you need to modify.

To reproduce: • Create a new gist. Click on ‘raw’. Get the url. Paste it in the racket code above. Up to now it should work ok. • Modify the gist. Click on ‘raw’. Chrome shows the updated version. Try the racket code again, there’s a good chance it will fail (though on rare occasions it worked immediately). After a few minutes (less than 5), the Racket code will get the updated version.


laurent.orseau
2021-3-22 14:13:04

{private browsing} No dice. Thanks for your help in any case!


greg
2021-3-22 14:17:10

> After a few minutes (less than 5), the Racket code will get the updated version Oh, interesting. If that info was above, sorry I overlooked it.

That sure “smells” like there’s a CDN involved, and it takes awhile for the gist server changes to flow through that. Perhaps based on that original server’s cache settings (which you can’t control), used by the CDN servers. I think?


greg
2021-3-22 14:17:25

As to how/why Chrome can get the updates, quicker, I have no idea, though!



soegaard2
2021-3-22 14:19:58

The first answer suggests that a delay is normal. But a more official reference would be nice.



soegaard2
2021-3-22 14:22:29

Didn’t even notice that comment!


laurent.orseau
2021-3-22 14:23:39

Thanks all the same @soegaard2 :smile:


laurent.orseau
2021-3-22 14:24:11

(a little too late unfortunately, I finished the video, although it took me hours longer than necessary… it may still be useful later anyway, for the same problem)


laurent.orseau
2021-3-22 14:27:02

The trick seems so obvious in hindsight!


laurent.orseau
2021-3-22 14:35:18

for the ephemeral record, here’s a version that should work generally: [...] ~Let me know if it doesn’t seem robust enough~ Edit: Hmm, I forgot that the ‘fragment’ comes after the params, so this is not good enough This should do it: https://github.com/Metaxal/bazaar/blob/master/net.rkt#L13


greg
2021-3-22 15:11:17

Nice! (I’d probably use current-seconds instead of (pseudo) random. But it probably doesn’t matter.)


greg
2021-3-22 15:13:13

I’m still curious how/why Chrome seemed to reliably get the latest version, quicker. Is that intentional? Is it accidentally a consequence of something else Chrome does differently (e.g. using HTTP3?) interacting with the CDN? So that might be fun to investigate more if I had time but I don’t right now. :slightly_smiling_face:


laurent.orseau
2021-3-22 15:14:35

Bogdan suggests this may be because it uses its own DNS resolution


laurent.orseau
2021-3-22 15:15:27

current-milliseconds would be better, as it’s possible to send several requests in the same second I guess. ~But that should be fine since it’s only temporary caching anyway.~ Though if one sets the random seed, that breaks. I’ll update. Good point!


samdphillips
2021-3-22 16:41:21

I think minimal Racket is there, but as soon as you pull in a package that requires scribble then you get a lot more.


samdphillips
2021-3-22 16:51:38

Oops I just caught up to the part where you mention that :stuck_out_tongue:


jesse697
2021-3-22 17:15:11

right — scribble is the main culprit there. A fully “no docs, no graphics” custom install is what I’d like.


jesse697
2021-3-22 17:16:35

It may not really be possible — there may well be some stuff that depends on Scribble in a “non-doc” way. And “no DrRacket” may also prove elusive since who knows what graphics functionally might actually be needed.


chansey97
2021-3-22 17:33:54

I have a very weird PL question, I don’t know if I can post it here. If you think this question is garbage, please down vote it. I will delete it immediately.

The question is: There are two mainstream scoping mechanisms in current programming languages: static scoping vs. dynamic scoping.

For example: static scoping (define (static-scoping) (let ((a 5)) (let ((p (λ () a)) (a 10)) (p)))) (static-scoping) ;; =&gt; 5 dynamic scoping (define (dynamic-scoping) (let ((a 5)) (let ((p (λ () a)) (a 10)) (p)))) (dynamic-scoping) ;; =&gt; 10 But what about this scoping mechanism as following? weird scoping (define (weird-scoping) (let ((a 5)) (let ((p (λ () (λ () a))) (a 15)) (let ((p (p)) ; &lt;--- the free variables in lambda be bound when the closure being created and using dynamic environment (a 10)) (p))))) (weird-scoping) ;; =&gt; 15 Is this weird-scoping make sense?


soegaard2
2021-3-22 17:36:53

This gives 15: (define (weird-scoping) (let ((a 5)) (letrec ((p (λ () (λ () a))) (a 15)) (let ((p (p)) (a 10)) (p)))))


chansey97
2021-3-22 17:44:17

@soegaard2 Your code is great but their semantics are different. In weird-scoping, the free variables in lambda be bound when the closure being created and using dynamic environment.


samth
2021-3-22 17:50:18

Can you give an example that distinguishes them?


chansey97
2021-3-22 17:55:51

What about: (define (weird-scoping) (let ((a 5)) (let ((p (λ () (λ () a)))) (let* ((a 15) (p1 (p)) (a 10) (p2 (p))) (println (p1)) ; print 15 (println (p2)) ; print 10 ) )))


samth
2021-3-22 17:58:03

That is indeed weird


chansey97
2021-3-22 18:00:00

I’m just curious, no one discovered this scoping mechanism before?


laurent.orseau
2021-3-22 18:06:12

It seems even more complicated to track mentally than dynamic scoping :slightly_smiling_face:


greg
2021-3-22 18:06:31

I think of this as overloading let to mean both lexical scope and dynamic scope, but in Racket we’d normally use let for the former and parameterize for the latter.


greg
2021-3-22 18:07:10

Like I think it’s effectively: (define (weird-scoping) (let ((a (make-parameter 5))) (let ((p (λ () (let ([x (a)]) (λ () x))))) (parameterize ([a 15]) (let ([p1 (p)]) (parameterize ([a 10]) (let ([p2 (p)]) (println (p1)) ; print 15 (println (p2)) ; print 10 )))))))


greg
2021-3-22 18:07:34

Or maybe more idiomatically (define (weird) (define a (make-parameter 5)) (define (make-p [v (a)]) (λ () v)) (define p1 (parameterize ([a 15]) (make-p))) (define p2 (parameterize ([a 10]) (make-p))) (println (p1)) (println (p2)))


greg
2021-3-22 18:08:28

And I’d prefer it to be more explicit, like that? :slightly_smiling_face: But I might be misunderstanding something else you’re getting at.


greg
2021-3-22 18:10:46

I mean in this toy example I’d prefer make-p take the value directly; no need for parameters. But in some cases there are N such things, and it can be easier.


ryanc
2021-3-22 18:11:25

I once implemented a similar feature in an interpreter. IIRC I had a variant of lambda that took an “aperture” list of variables to acquire from the caller’s environment. If the aperture is empty, you get an ordinary closure. If it’s “everything”, you get dynamic scoping (for that call, anyway). Syntactic closures, an old macro hygiene algorithm, were based on a similar idea. Kind of.


ryanc
2021-3-22 18:14:06

Oh, now I remember another grad student at the time was working on a language for “environmental acquisition”, which was also somewhat related, so that may have made me think about that generalization.


greg
2021-3-22 18:14:57

I mean Emacs Lisp has dynamic scope plus lexical scope. At first it only had the former. Now you can enable lexical scope in a file, and defvar vars still get the dynamic treatment. It’s backward compatible, it works, and it’s not horrible. But I’m not sure it’s the best way, clean sheet of paper.


greg
2021-3-22 18:15:35

(All using let)


chansey97
2021-3-22 18:20:12

I think your Parameters code can express my intention.


sorawee
2021-3-22 18:22:52

With weird scoping, what would be the result of

(define (weird-scoping) (let ((a 5)) (let ((p (λ () a)) (a 10)) (p)))) (weird-scoping) ?


chansey97
2021-3-22 18:23:37

Is 5


sschwarzer
2021-3-22 20:58:49

Is there a compile-time way to compile different code for different platforms, say, Windows vs. Linux? With “compile” I mean having different module top level defines, for example.


sschwarzer
2021-3-22 21:16:04

In a command line program, I raise an exception for user errors. I handle those with (with-handlers ([exn:fail? (lambda (exn) (eprintf "~a\n" (exn-message exn)))]) (main)) which gives for an unknown file open-input-file: cannot open input file path: /home/.../todoreport/crd,desc system error: No such file or directory; errno=2 Since the error message is intended for users, not Racket programmers, I’d like to strip off the “open-input-file: ” part (the name of the function). Should I do this with a string operation like removing everything until and including the first ": " or is there a more elegant way?


badkins
2021-3-22 21:24:11

It appears string-split is at least twice as slow as it needs to be for the (probably very common) case of splitting on whitespace. In that case, no regular expressions are needed, and trim? would be #t, so would it make sense to change: (define (string-split str [sep none] #:trim? [trim? #t] #:repeat? [+? #f]) (internal-split 'string-split str sep trim? +?)) to: (define (string-split str [sep none] #:trim? [trim? #t] #:repeat? [+? #f]) (if (eq? sep none) (internal-split-whitespace str) ; &lt;== doesn't exist yet (internal-split 'string-split str sep trim? +?))) However, that wouldn’t handle the case of explicitly specifying a whitespace regex.


badkins
2021-3-22 21:26:49

The motivation for this is me coding up a solution to <https://benhoyt.com/writings/count-words/|a simple problem> and discovering that string-split was a significant portion of the time. I coded up an off-the-top-of-my-head string-split replacement, and the program ran twice as fast. Given that string-split wasn’t 100% of the execution time, it seems my rough draft replacement is more than twice as fast.


badkins
2021-3-22 21:28:14

badkins
2021-3-22 21:36:32

Or maybe change: sep : (or/c string? regexp?) = #px"\\s+" to sep : (or/c 'whitespace string? regexp?) = 'whitespace Someone could thwart the optimization by explicitly passing #px"\\s+", but in that case they might be asking for it a little.


laurent.orseau
2021-3-22 21:42:41

Instead of a specific 'whitespace', how about taking a list and/or range of characters? Or even (list-of (or/c char? (list/c range-start range-end))` ?


badkins
2021-3-22 21:44:21

That would make it more general, but I do think whitespace may be important enough for a special optimization.


laurent.orseau
2021-3-22 21:45:09

Possibly, depends on whether this special case really needs to be special


laurent.orseau
2021-3-22 21:46:43

(and now I’m wondering, given a set of characters as integers, design/compile the minimum number of bit tests to decide whether a given character is in the set)


badkins
2021-3-22 21:47:19

I think one important thing is to gain this optimization w/o changing any client code.


badkins
2021-3-22 21:47:43

i.e. all occurrences of (string-split str) would immediately get the benefit.


badkins
2021-3-22 21:48:34

being able to specify what you’re suggesting, could be a nice addition, but separate from what I’m thinking of.


laurent.orseau
2021-3-22 21:49:16

In that case, can’t you just check whether the sep is equal? to #px"\s+" ?


badkins
2021-3-22 21:50:32

Not exactly, because the default is actually none :) But yes, you could check for (or (eq? sep none) (equal? sep #px"\\s+")) - I’d have to research regex equality.


laurent.orseau
2021-3-22 21:52:47

That way you don’t need to change the signature


badkins
2021-3-22 21:53:55

The signature wouldn’t change with my approach, but the documentation might be clearer by specifying 'whitespace as the default.


badkins
2021-3-22 21:56:57

Internally in my code I’m using char-whitespace? and (compose not char-whitespace?), so I suppose another option would be using a predicate for sep


sschwarzer
2021-3-22 21:59:33

Is there a Racket function in the standard library to wrap text to multiple lines (in one string or a list of strings), similar to Python’s https://docs.python.org/3/library/textwrap.html ? I’d like to use this for multiline help output for an option of a command-line program. I checked https://docs.racket-lang.org/reference/strings.html and https://docs.racket-lang.org/reference/Writing.html , but didn’t find anything helpful.


samdphillips
2021-3-22 22:00:14

A cleaner way would be to test for the existence of the file and trigger your own behavior. Which I understand is annoying.


samdphillips
2021-3-22 22:00:41

You could try and catch the specific exception and make a better exception (or error message)


sschwarzer
2021-3-22 22:00:52

I guess it’s not too difficult to implement this myself, but I wouldn’t want to reinvent the wheel unless it’s necessary. :slightly_smiling_face:


samdphillips
2021-3-22 22:01:45

Something like: (define (do-something-with-file fname) (with-handlers ([exn:fail:filesystem:errno? (lambda (e) (really-check-this-file-not-found-and-error e fname))]) (open-input-file fname)))


samdphillips
2021-3-22 22:03:19

I looks like that exception contains OS specific magic numbers and it doesn’t provide the filename so you’d have to get it some other way.


samdphillips
2021-3-22 22:07:21

Oh and the exception doesn’t store any of the useful information in fields, so you are either string munging or reimplementing stuff :(


samdphillips
2021-3-22 22:12:10

fprintf has some formats, but I think they are for truncating not wrapping https://docs.racket-lang.org/reference/Writing.html?q=fprintf#%28def._%28%28quote._~23~25kernel%29._fprintf%29%29 There is also pretty-print and also the racket/format library.


sschwarzer
2021-3-22 22:40:14

@samdphillips Thanks for your feedback. Probably the errno isn’t helpful for users, but I’d like to keep the other information about what’s wrong with the path. (In the example, the path is complete, I just shortened it.) The actual problem could be a file not found, but it could also be a permission problem. The current text (hopefully) uses the actual reason why the path is problematic. As you say, there are no exception fields with the detail information to make it easy to create my own message.


badkins
2021-3-22 22:43:10

I’ve been doing some text processing recently, and having something like this might be handy, so I coded up a quick experiment: #lang racket/base (require racket/string) (define (string-wrap str width) (let loop ([ words (string-split str) ][ line "" ][ result '() ]) (let ([ line-len (string-length line) ]) (if (null? words) (reverse (if (&gt; line-len 0) (cons line result) result)) (let ([ word (car words) ]) (if (&gt; (+ line-len 1 (string-length word)) width) (loop (cdr words) word (cons line result)) (loop (cdr words) (if (&gt; line-len 0) (string-append line " " word) word) result)))))))


sschwarzer
2021-3-22 22:49:41

@samdphillips From what I understand in the fprintf documentation, the function doesn’t wrap text. Neither anything in pretty-print or racket/format seems to provide the functionality.


samdphillips
2021-3-22 22:50:29

racket/format doesn’t wrap, but a lot of the other formatting functionality is there.


mflatt
2021-3-22 22:51:43

I’m in favor of this change. I don’t have a strong opinion about how to trigger the fast case, but I’d probably just trigger it just when no sep is provided.

In case it’s useful, here’s code I wrote once in an effort to make string-split fast.

(define (string-split s) (let loop ([start 0] [end 0]) (cond [(= end (string-length s)) (if (= start end) '() (list (substring s start end)))] [(char-whitespace? (string-ref s end)) (let ([p (add1 end)]) (if (= start end) (loop p p) (cons (substring s start end) (loop p p))))] [else (loop start (add1 end))])))


badkins
2021-3-22 22:52:22

:+1:


badkins
2021-3-22 22:52:51

This will be a good excuse for me to get setup to build from source & issue my first pull request :)


sschwarzer
2021-3-22 22:54:23

@badkins This seems to work, thanks.


sschwarzer
2021-3-22 22:55:03

@samdphillips Yes, I guess I’ll come back to racket/format now and then. :slightly_smiling_face:


badkins
2021-3-22 22:55:24

There may be some corner cases to workout. For example, I just found: (string-wrap "abc" 1) ; ==&gt; '("" "abc")


badkins
2021-3-22 22:56:23

…but that is a bit pathological


badkins
2021-3-22 22:57:37

Here’s a fix for that: #lang racket/base (require racket/string) (define (string-wrap str width) (let loop ([ words (string-split str) ][ line "" ][ result '() ]) (let ([ line-len (string-length line) ]) (if (null? words) (reverse (if (&gt; line-len 0) (cons line result) result)) (let ([ word (car words) ]) (if (&gt; (+ line-len 1 (string-length word)) width) (loop (cdr words) word (if (&gt; line-len 0) (cons line result) result)) (loop (cdr words) (if (&gt; line-len 0) (string-append line " " word) word) result)))))))


sschwarzer
2021-3-22 22:58:23

At the moment, I have the lines hardcoded in the command-line call. (I wrote this after posting the question here.) That’s ok-ish for now. #:ps "" "Group and sort specs are of the form &lt;order&gt;&lt;field-abbreviation&gt; ." "" "&lt;order&gt; is \"+\" or \"-\" for ascending or descending order, respectively." "&lt;order&gt; can be omitted, in which case ascending order is assumed." "" "&lt;field-abbreviation&gt; is one of the following task fields to group or sort by:" " x completed" " pri or prio priority" " cod completion date" etc. It turned out I need quite a bit custom formatting beyond line breaks anyway.


sschwarzer
2021-3-22 22:59:04

@badkins Would be great to have something like this in the standard library. :slightly_smiling_face:


mflatt
2021-3-22 22:59:46

You could use (system-type) at expansion time in a macro.

That approach is discouraged, because fully expanded code is meant to be platform-independent, which in turn enables a platform-independent “.zo” form (although platform-independent form is no longer the default with CS). For libraries that need to work well with packaging and distribution tools, normally the best option is to put the code in different modules and dynamic-require the right one at run time (or generally make the selection dynamic instead of static). There is a concept of platform-specific packages so that irrelevant packages can be avoided for a platform, but normally those packages are used in combination with things like dynamic-require.

But if packaging is not important, then a macro that uses (system-type) can be an ok choice.


sschwarzer
2021-3-22 23:05:18

@mflatt Thanks! I might need platform-specific paths and environment variable names in a future project. I’ll probably use the dynamic selection, or, if this doesn’t work well enough, I’ll go with the dynamic-require approach.


mflatt
2021-3-22 23:07:18

As you say, compiler/embed more or less assumes that it’s running from a Racket distribution instead of an embedding application, and it won’t be able to find embedded modules to embed in a new application. You may have more success with --collects-dest to build up a kind of snapshot of relevant “.zo” files, but I doubt that there’s an easy way right now to get all the support files (such as the Racket executable) in a nice place for compiler/embed to find.


phillip
2021-3-23 00:58:01

Yes, thanks for this. I actually got it working between my hack to find-exe and using --collects-dest. I also had to do some trickery with the pkgs folder. It doesn’t seem there is a command line argument or function to call to override the pkgs location?


mflatt
2021-3-23 01:06:23

Right, there’s not. That’s only configured through “config.rktl” right now.


jaz
2021-3-23 01:07:05

There was some talk about this on the mailing list a long while back. My 2 cents is that if you want to split on a regex, you have regexp-split. For trimming whitespace, you should have dedicated functions.


jaz
2021-3-23 01:26:57

Though… that doesn’t address splitting on whitespace. And I guess it’s pretty common to have a function that treats that as a special case.


greg
2021-3-23 03:26:18

There’s a wrap-line provided by scribble/text/wrap. It’s not documented but it’s commented pretty well: https://github.com/racket/scribble/blob/master/scribble-text-lib/scribble/text/wrap.rkt


greg
2021-3-23 03:26:54

I only remembered this from having seen it used in xrepl, where it changes current-output-port to a port that automatically wraps output: https://github.com/racket/xrepl/blob/master/xrepl-lib/xrepl/xrepl.rkt#L185-L231


greg
2021-3-23 03:30:44

So you can get wrap-line with a simple (require scribble/text/wrap). It’s not like it’s under a private/ subdir. So in that sense it’s “in the standard library”. But it’s not documented. If you find it works well, maybe someone (you?) could volunteer to write scribble docs for it?


greg
2021-3-23 03:32:23

Maybe there’s a discussion that it should move to racket/string to be more discoverable (and then scribble/text/wrap could just re-provide it). Maybe even similar for the “wrapping port” that xrepl builds around it. idk


rokitna
2021-3-23 06:26:11

I’ve tinkered a little bit with the idea of making a generic interface for quotable values, with a method to convert the value into an expression. And I’m starting to think it’d be nice to have an expression where most things were quotable (but where it’s understood that it can breach encapsulation, so either it isn’t done that often or the interface is protected somehow). There are some languages out there that have serializable closures, so this probably wouldn’t break new ground, but it might take a bit of a departure from the usual Racket infrastructure.


kellysmith12.21
2021-3-23 06:35:18

@rokitna it turns out that @notjack was telling me about a similar idea (unless I’ve misunderstood someone)