Re: [racket-dev] promise vs polym contracts

2012-07-12 Thread Matthias Felleisen

On Jul 10, 2012, at 11:33 PM, Eli Barzilay wrote:

 On Saturday, Matthias Felleisen wrote:
 
 The more general idea is that there is an alternative design space
 out there where you want 'boxes' that signal errors when touched by
 strict functions. You need these every time you want transparent
 transitions from one point in the computational spectrum to another.
 
 ;; ---
 
 And one more level up, I am thinking of selling Racket as a
 wide-spectrum programming language, the first one that introduces
 safe or transparent transitions properly.
 
 To combine both of these and my general wish for how the lazy language
 would evolve: it would be nice beyond words if there was some new
 implicit `#%value' macro wrapped around places that require a value.
 With that, an implementation of a lazy language would be trivial,
 together with all of the related similar-in-spirit languages.
 
 (To relate this to another recent thing -- having a no-value thing
 that doesn't leak out is too restrictive for some uses (as I need to
 write in the followup I promised), but a way to loosen that
 restriction so you can pass around no-values and avoid a damaging leak
 would be to make such a `#%value' check for them...  But that's of
 course just vague hand-waving that would naively suffer from the same
 perfomance problems...)

Eli's write-up reminded me that we need these things for MultiLisp-tyle
futures, too. And yes, #%value would be the right thing in a small model
I just don't know how to add this to a language with primitive functions. 
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-10 Thread Eli Barzilay
On Friday, Carl Eastlund wrote:
 
 From in-person conversation, what's really going on here is that
 Matthias is talking about some other, imaginary language, not
 Racket, where _any_ primitive operation other than promise? forces
 any promises in its arguments.  In this language, promises are more
 like an implicit monad than a data structure.

To make it more concrete in a possibly more obscure way, I think that
he's talking about a world where you only need `lazy', not `delay'.
(Since the only point of the latter is being able to distinguish a
promise that forces to a promise.)  To put it differently, you don't
have to switch to the world where promises are forced implicitly
(that's the r5rs blurb that I said many times that it should be
removed since it's much more than just a footnote to a language), you
just need a world where being a `promise?' is something that concerns
only meta/introspective kind of code, as in Robby's followp.


On Saturday, Matthias Felleisen wrote:
 
 The more general idea is that there is an alternative design space
 out there where you want 'boxes' that signal errors when touched by
 strict functions. You need these every time you want transparent
 transitions from one point in the computational spectrum to another.
 
 ;; ---
 
 And one more level up, I am thinking of selling Racket as a
 wide-spectrum programming language, the first one that introduces
 safe or transparent transitions properly.

To combine both of these and my general wish for how the lazy language
would evolve: it would be nice beyond words if there was some new
implicit `#%value' macro wrapped around places that require a value.
With that, an implementation of a lazy language would be trivial,
together with all of the related similar-in-spirit languages.

(To relate this to another recent thing -- having a no-value thing
that doesn't leak out is too restrictive for some uses (as I need to
write in the followup I promised), but a way to loosen that
restriction so you can pass around no-values and avoid a damaging leak
would be to make such a `#%value' check for them...  But that's of
course just vague hand-waving that would naively suffer from the same
perfomance problems...)

-- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-07 Thread Matthias Felleisen

As Carl pointed out, I should explain more background. 

Stephen and I are exploring how to get laziness in Racket w/o going 
to Lazy Racket (in a sense). As I wrote the idea is to introduce laziness
in a somewhat transparent manner, the way Sam introduces types in a mostly
transparent manner (minus polymorphic exports and bugs). 

What this particularly means is that I do not want programs to change
behavior silently. So yes you get promise? and you get force so that 
you can retrieve computations as needed. BUT it becomes your responsibility 
to force things for all strict functions/points in your language (e.g., 
add1, function application, if). If you forget to force there or insert 
a promise?, the language traps the value. 

;; --- 

The more general idea is that there is an alternative design space 
out there where you want 'boxes' that signal errors when touched by 
strict functions. You need these every time you want transparent 
transitions from one point in the computational spectrum to another. 

;; ---

And one more level up, I am thinking of selling Racket as a wide-spectrum
programming language, the first one that introduces safe or transparent
transitions properly. 

-- Matthias, CPS 

p.s. Sam's version forces too early because strict functions should 
force not variable lookups. This is a common misconception among programmers. 
I suspect it goes back to Abelson and Sussman getting it wrong (see what 
happens if you don't understand the foundations). 

Then again, Racket is PITCO so in a sense the force is executed at the right 
place in this particular example, even though it was inserted in the wrong 
place. 









On Jul 6, 2012, at 8:19 PM, Robby Findler wrote:

 Lets say I have a function that gets a list of promises or lists. It
 is going to print out the state of some ongoing computation (that is
 producing these lists). It will print the list, if there's a list, and
 it will print pending if it is a promise; it doesn't want to force
 it, since it is just a view.
 
 Somewhere else, something is building new lists-- when it decides to
 force something, it replaces the promise in the list with the forced
 value and then hands a new list off to the view.
 
 (But in general, it just seems useful to be able to ask if something
 is a promise without forcing it.)
 
 Robby
 
 On Fri, Jul 6, 2012 at 4:53 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:
 
 I can't think of such a primitive other than force, for which it is okay. 
 Can you be concrete?
 
 
 
 On Jul 6, 2012, at 5:16 PM, Robby Findler wrote:
 
 What do you do if you have a function that accepts either promises or
 lists? Then, you might want total predicates.
 
 Robby
 
 On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:
 
 I just realized that Racket already suffers from the problem that 
 polymorphic contracts introduce.
 
 As Stephen is working out right now, Racketeers want to introduce laziness 
 to speed up programs on occasion. We have been told for decades that delay 
 and force are our friends. In a sense, this performance-refactoring 
 problem is exactly the same problem as incremental type refactoring aka 
 gradual typing. You want to add laziness in a transparent manner -- or if 
 you make a mistake, it should blow up on you.
 
 But it doesn't:
 
 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
 (null? (delay (/ 1 0)))
 #f
 (zero? (delay (/ 1 0)))
 . . zero?: contract violation
 expected: number?
 given: #promise:unsaved-editor12957:6:9
 
 For some reasons I don't understand, our ancestors (let's not use their 
 name anymore) decided to make some primitives resistant to promises and 
 some aren't. Now imagine you accidentally package a null in a delay, which 
 may happen when you use lazy combinators:
 
 (null? (delay null))
 #f
 
 Your program changes meaning and off it goes and signals an error. You 
 don't get a faster program, you get a program that raises the wrong kind 
 of error.
 
 What they should have done is signal an exception when strict primitives 
 receive a promise.
 
 I take it is too late to correct such a {\HUGE HUGE} historical blunder. 
 -- Matthias
 
 
 _
 Racket Developers list:
 http://lists.racket-lang.org/dev
 


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-07 Thread Matthias Felleisen

I may have made a mistake under time pressure: 

 [else (+ (force (sum (car x))) (force (sum (cdr x]





On Jul 7, 2012, at 10:19 AM, Sam Tobin-Hochstadt wrote:

 On Sat, Jul 7, 2012 at 10:14 AM, Matthias Felleisen
 matth...@ccs.neu.edu wrote:
 
 p.s. Sam's version forces too early because strict functions should
 force not variable lookups. This is a common misconception among programmers.
 I suspect it goes back to Abelson and Sussman getting it wrong (see what
 happens if you don't understand the foundations).
 
 I still don't think my version forces too early -- your alternative
 contains no calls to `force` at all.  Are you suggesting that I should
 `force` around the recursive calls to `sum`?  In that case, clients
 are exposed to promises, and the result type is (U Integer (Promise
 Integer)).  I can't tell from your description if you think that's an
 acceptable result.
 -- 
 sam th
 sa...@ccs.neu.edu


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-07 Thread Matthias Felleisen


On Jul 7, 2012, at 10:27 AM, Sam Tobin-Hochstadt wrote:

 Right, that's what I thought.  This is only different in that:
 
(define v (delay (+ 1 2)))
(sum v) ; = v
 
 Which means clients of `sum` now have to handle either Integers or
 Promises.  Is that intentional?


Yes. 

;; --- 

Overall I feel like Bob Harper when he called me to say that 
he had figured out ML got it all wrong with let after he had
read and understood the draft of Andrew's value restriction 
paper I had mailed him. Here is how he put it -- Omega has 
the wrong type on the right side of the let, you just don't 
notice. 

The real idea, which he developed a year later or so, was to 
map ML to a language with Lambda and then you see that let 
should be translated like this 

 [[ let x = e in e' ]] . = let x = Lambda it. [[e]]. in [[e']].

where it is the inferred possibly forall-quantified type. Now
you see that old ML shifted the semantics for Omega (and ref
and callcc etc) w/o knowing it. In the purely functional case --
which everyone uses for papers and stuff -- you don't notice
the shift from bottom to functional bottom or (which is what
I conjectured) there are errors in old proofs. 

-- Matthias, CPS 






 
 On Sat, Jul 7, 2012 at 10:23 AM, Matthias Felleisen
 matth...@ccs.neu.edu wrote:
 
 I may have made a mistake under time pressure:
 
 [else (+ (force (sum (car x))) (force (sum (cdr x]
 
 
 
 
 
 On Jul 7, 2012, at 10:19 AM, Sam Tobin-Hochstadt wrote:
 
 On Sat, Jul 7, 2012 at 10:14 AM, Matthias Felleisen
 matth...@ccs.neu.edu wrote:
 
 p.s. Sam's version forces too early because strict functions should
 force not variable lookups. This is a common misconception among 
 programmers.
 I suspect it goes back to Abelson and Sussman getting it wrong (see what
 happens if you don't understand the foundations).
 
 I still don't think my version forces too early -- your alternative
 contains no calls to `force` at all.  Are you suggesting that I should
 `force` around the recursive calls to `sum`?  In that case, clients
 are exposed to promises, and the result type is (U Integer (Promise
 Integer)).  I can't tell from your description if you think that's an
 acceptable result.
 --
 sam th
 sa...@ccs.neu.edu
 
 
 
 
 -- 
 sam th
 sa...@ccs.neu.edu


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


[racket-dev] promise vs polym contracts

2012-07-06 Thread Matthias Felleisen

I just realized that Racket already suffers from the problem that polymorphic 
contracts introduce. 

As Stephen is working out right now, Racketeers want to introduce laziness to 
speed up programs on occasion. We have been told for decades that delay and 
force are our friends. In a sense, this performance-refactoring problem is 
exactly the same problem as incremental type refactoring aka gradual typing. 
You want to add laziness in a transparent manner -- or if you make a mistake, 
it should blow up on you. 

But it doesn't: 

 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
  (null? (delay (/ 1 0)))
 #f
  (zero? (delay (/ 1 0)))
 . . zero?: contract violation
   expected: number?
   given: #promise:unsaved-editor12957:6:9

For some reasons I don't understand, our ancestors (let's not use their name 
anymore) decided to make some primitives resistant to promises and some aren't. 
Now imagine you accidentally package a null in a delay, which may happen when 
you use lazy combinators: 

  (null? (delay null))
 #f

Your program changes meaning and off it goes and signals an error. You don't 
get a faster program, you get a program that raises the wrong kind of error. 

What they should have done is signal an exception when strict primitives 
receive a promise. 

I take it is too late to correct such a {\HUGE HUGE} historical blunder. -- 
Matthias


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-06 Thread Robby Findler
What do you do if you have a function that accepts either promises or
lists? Then, you might want total predicates.

Robby

On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu wrote:

 I just realized that Racket already suffers from the problem that polymorphic 
 contracts introduce.

 As Stephen is working out right now, Racketeers want to introduce laziness to 
 speed up programs on occasion. We have been told for decades that delay and 
 force are our friends. In a sense, this performance-refactoring problem is 
 exactly the same problem as incremental type refactoring aka gradual typing. 
 You want to add laziness in a transparent manner -- or if you make a mistake, 
 it should blow up on you.

 But it doesn't:

 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
  (null? (delay (/ 1 0)))
 #f
  (zero? (delay (/ 1 0)))
 . . zero?: contract violation
   expected: number?
   given: #promise:unsaved-editor12957:6:9

 For some reasons I don't understand, our ancestors (let's not use their name 
 anymore) decided to make some primitives resistant to promises and some 
 aren't. Now imagine you accidentally package a null in a delay, which may 
 happen when you use lazy combinators:

  (null? (delay null))
 #f

 Your program changes meaning and off it goes and signals an error. You don't 
 get a faster program, you get a program that raises the wrong kind of error.

 What they should have done is signal an exception when strict primitives 
 receive a promise.

 I take it is too late to correct such a {\HUGE HUGE} historical blunder. -- 
 Matthias


 _
   Racket Developers list:
   http://lists.racket-lang.org/dev

_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-06 Thread Matthias Felleisen

I can't think of such a primitive other than force, for which it is okay. Can 
you be concrete? 



On Jul 6, 2012, at 5:16 PM, Robby Findler wrote:

 What do you do if you have a function that accepts either promises or
 lists? Then, you might want total predicates.
 
 Robby
 
 On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:
 
 I just realized that Racket already suffers from the problem that 
 polymorphic contracts introduce.
 
 As Stephen is working out right now, Racketeers want to introduce laziness 
 to speed up programs on occasion. We have been told for decades that delay 
 and force are our friends. In a sense, this performance-refactoring problem 
 is exactly the same problem as incremental type refactoring aka gradual 
 typing. You want to add laziness in a transparent manner -- or if you make a 
 mistake, it should blow up on you.
 
 But it doesn't:
 
 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
 (null? (delay (/ 1 0)))
 #f
 (zero? (delay (/ 1 0)))
 . . zero?: contract violation
  expected: number?
  given: #promise:unsaved-editor12957:6:9
 
 For some reasons I don't understand, our ancestors (let's not use their name 
 anymore) decided to make some primitives resistant to promises and some 
 aren't. Now imagine you accidentally package a null in a delay, which may 
 happen when you use lazy combinators:
 
 (null? (delay null))
 #f
 
 Your program changes meaning and off it goes and signals an error. You don't 
 get a faster program, you get a program that raises the wrong kind of error.
 
 What they should have done is signal an exception when strict primitives 
 receive a promise.
 
 I take it is too late to correct such a {\HUGE HUGE} historical blunder. -- 
 Matthias
 
 
 _
  Racket Developers list:
  http://lists.racket-lang.org/dev


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-06 Thread Sam Tobin-Hochstadt
On Fri, Jul 6, 2012 at 5:53 PM, Matthias Felleisen matth...@ccs.neu.edu wrote:

 I can't think of such a primitive other than force, for which it is okay. Can 
 you be concrete?

Here's a type definition;

(define-type LTree
(U (Promise Integer) (Cons LTree LTree)))

This is just a tree of integer promises, but to traverse it, we need
to write code like:

(define (sum lt)
  (cond [(cons? lt) (+ (sum (car lt)) (sum (cdr lt)))]
   [else (force lt)]))

If `cons?` raised an error when applied to promises, we'd be out of luck.

 On Jul 6, 2012, at 5:16 PM, Robby Findler wrote:

 What do you do if you have a function that accepts either promises or
 lists? Then, you might want total predicates.

 Robby

 On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:

 I just realized that Racket already suffers from the problem that 
 polymorphic contracts introduce.

 As Stephen is working out right now, Racketeers want to introduce laziness 
 to speed up programs on occasion. We have been told for decades that delay 
 and force are our friends. In a sense, this performance-refactoring problem 
 is exactly the same problem as incremental type refactoring aka gradual 
 typing. You want to add laziness in a transparent manner -- or if you make 
 a mistake, it should blow up on you.

 But it doesn't:

 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
 (null? (delay (/ 1 0)))
 #f
 (zero? (delay (/ 1 0)))
 . . zero?: contract violation
  expected: number?
  given: #promise:unsaved-editor12957:6:9

 For some reasons I don't understand, our ancestors (let's not use their 
 name anymore) decided to make some primitives resistant to promises and 
 some aren't. Now imagine you accidentally package a null in a delay, which 
 may happen when you use lazy combinators:

 (null? (delay null))
 #f

 Your program changes meaning and off it goes and signals an error. You 
 don't get a faster program, you get a program that raises the wrong kind of 
 error.

 What they should have done is signal an exception when strict primitives 
 receive a promise.

 I take it is too late to correct such a {\HUGE HUGE} historical blunder. -- 
 Matthias


 _
  Racket Developers list:
  http://lists.racket-lang.org/dev


 _
   Racket Developers list:
   http://lists.racket-lang.org/dev



-- 
sam th
sa...@ccs.neu.edu

_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-06 Thread Carl Eastlund
On Fri, Jul 6, 2012 at 6:53 PM, Matthias Felleisen matth...@ccs.neu.edu wrote:

 I don't care about typed definitions.

Sam didn't have a typed definition, just a type definition, note
the significant d suffix.

 And yes, in an untyped world, you'd write

 (define (sum lt)
   (cond
 [(promise? lt) lt]
 [else (+ (sum (car lt)) (sum (cdr lt)))]))

In an untyped world, you could write the same function that Sam did.

 Note the mistake you made in forcing too early.

Sam didn't force anything too early.  He only forced anything in the
branch that accepts promises.

-

From in-person conversation, what's really going on here is that
Matthias is talking about some other, imaginary language, not Racket,
where _any_ primitive operation other than promise? forces any
promises in its arguments.  In this language, promises are more like
an implicit monad than a data structure.  I don't know why Matthias
has not clarified this issue so far, but I'm doing so now to hopefully
save some bandwidth.

--Carl

 On Jul 6, 2012, at 5:58 PM, Sam Tobin-Hochstadt wrote:

 On Fri, Jul 6, 2012 at 5:53 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:

 I can't think of such a primitive other than force, for which it is okay. 
 Can you be concrete?

 Here's a type definition;

 (define-type LTree
(U (Promise Integer) (Cons LTree LTree)))

 This is just a tree of integer promises, but to traverse it, we need
 to write code like:

 (define (sum lt)
  (cond [(cons? lt) (+ (sum (car lt)) (sum (cdr lt)))]
   [else (force lt)]))

 If `cons?` raised an error when applied to promises, we'd be out of luck.

 On Jul 6, 2012, at 5:16 PM, Robby Findler wrote:

 What do you do if you have a function that accepts either promises or
 lists? Then, you might want total predicates.

 Robby

 On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:

 I just realized that Racket already suffers from the problem that 
 polymorphic contracts introduce.

 As Stephen is working out right now, Racketeers want to introduce 
 laziness to speed up programs on occasion. We have been told for decades 
 that delay and force are our friends. In a sense, this 
 performance-refactoring problem is exactly the same problem as 
 incremental type refactoring aka gradual typing. You want to add laziness 
 in a transparent manner -- or if you make a mistake, it should blow up on 
 you.

 But it doesn't:

 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
 (null? (delay (/ 1 0)))
 #f
 (zero? (delay (/ 1 0)))
 . . zero?: contract violation
 expected: number?
 given: #promise:unsaved-editor12957:6:9

 For some reasons I don't understand, our ancestors (let's not use their 
 name anymore) decided to make some primitives resistant to promises and 
 some aren't. Now imagine you accidentally package a null in a delay, 
 which may happen when you use lazy combinators:

 (null? (delay null))
 #f

 Your program changes meaning and off it goes and signals an error. You 
 don't get a faster program, you get a program that raises the wrong kind 
 of error.

 What they should have done is signal an exception when strict primitives 
 receive a promise.

 I take it is too late to correct such a {\HUGE HUGE} historical blunder. 
 -- Matthias


 _
 Racket Developers list:
 http://lists.racket-lang.org/dev


 _
  Racket Developers list:
  http://lists.racket-lang.org/dev



 --
 sam th
 sa...@ccs.neu.edu


 _
   Racket Developers list:
   http://lists.racket-lang.org/dev

_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] promise vs polym contracts

2012-07-06 Thread Robby Findler
Lets say I have a function that gets a list of promises or lists. It
is going to print out the state of some ongoing computation (that is
producing these lists). It will print the list, if there's a list, and
it will print pending if it is a promise; it doesn't want to force
it, since it is just a view.

Somewhere else, something is building new lists-- when it decides to
force something, it replaces the promise in the list with the forced
value and then hands a new list off to the view.

(But in general, it just seems useful to be able to ask if something
is a promise without forcing it.)

Robby

On Fri, Jul 6, 2012 at 4:53 PM, Matthias Felleisen matth...@ccs.neu.edu wrote:

 I can't think of such a primitive other than force, for which it is okay. Can 
 you be concrete?



 On Jul 6, 2012, at 5:16 PM, Robby Findler wrote:

 What do you do if you have a function that accepts either promises or
 lists? Then, you might want total predicates.

 Robby

 On Fri, Jul 6, 2012 at 2:22 PM, Matthias Felleisen matth...@ccs.neu.edu 
 wrote:

 I just realized that Racket already suffers from the problem that 
 polymorphic contracts introduce.

 As Stephen is working out right now, Racketeers want to introduce laziness 
 to speed up programs on occasion. We have been told for decades that delay 
 and force are our friends. In a sense, this performance-refactoring problem 
 is exactly the same problem as incremental type refactoring aka gradual 
 typing. You want to add laziness in a transparent manner -- or if you make 
 a mistake, it should blow up on you.

 But it doesn't:

 Welcome to DrRacket, version 5.3.0.13--2012-07-05(467bde3a/d) [3m].
 Language: racket.
 (null? (delay (/ 1 0)))
 #f
 (zero? (delay (/ 1 0)))
 . . zero?: contract violation
  expected: number?
  given: #promise:unsaved-editor12957:6:9

 For some reasons I don't understand, our ancestors (let's not use their 
 name anymore) decided to make some primitives resistant to promises and 
 some aren't. Now imagine you accidentally package a null in a delay, which 
 may happen when you use lazy combinators:

 (null? (delay null))
 #f

 Your program changes meaning and off it goes and signals an error. You 
 don't get a faster program, you get a program that raises the wrong kind of 
 error.

 What they should have done is signal an exception when strict primitives 
 receive a promise.

 I take it is too late to correct such a {\HUGE HUGE} historical blunder. -- 
 Matthias


 _
  Racket Developers list:
  http://lists.racket-lang.org/dev


_
  Racket Developers list:
  http://lists.racket-lang.org/dev