Re: [racket-users] Re: single-file packages and embedded documentation and metadata

2017-11-10 Thread Neil Van Dyke

Jack Firth wrote on 11/10/2017 11:32 PM:


I have to redo my tools once more, and am sick of my current `(doc
...)` and `@doc` format, *and the resulting dependency on McFly.*


Could you elaborate on your experience with this?


A snippet of an example of embedded-docs source file for 
"http://www.neilvandyke.org/racket/roomba/; is at the bottom of this 
file.  See the dependency on `mcfly`, which simply provides a dummy 
`doc` form so that the Racket compiler ignores everything in `doc`.


(I later added support for `at-exp` `@doc` syntax, which makes the 
documentation easier to type and look at than in that painful-looking 
example, and also visually differentiates it from code.)


The result works OK, but I have to write a new tool for other reasons, 
with no time to do so, and I'd like to simplify this.


For a module with embedded docs I'd want to treat them the same as 
tests: in a special submodule which results in "build-time" 
dependencies instead of runtime dependencies.


Yes, I'm sympathetic to something like that.

I've thought of having `doc` transform to `(module+ doc`, and having 
that then `provide` the data in some easily-computer-accessible form, as 
well as generate the contemporary Scribble manuals.


One reason I've been shying away from that is that it's not only more 
work (I don't really have time to write more tools, which distract from 
my actual work), but then it looks like it wants to be a core Racket 
thing, on something for which compromise would have to be found among 
people from different schools of thought and goals (like a standards 
effort; again, no time for this).  And I'm still subconsciously always 
figuring out the nuances to support the full-lifecycle story for my 
industry practitioner's book, which is for people with needs and 
thinking somewhat different than that of core Racket developers.  So, 
going with another quick-to-implement approach that lets me hit all the 
nuances I can, without creating big core Racket work for everyone, and 
seems the most likely way that I'll ever get all the nuances I want.


I suppose I could do this in a tractably quick way, without a big 
production, if I'm willing to keep a dependency on another package -- 
either for a `#lang` variant of `racket/base`, or maybe a `require` from 
`racket/base` and suboptimal syntax.  But that's more work, people won't 
like that dependency (I've had people fork my packages to remove the 
dependency before), and using `racket/base` seems most friendly for 
sharing reusable open source modules.


Which is why I'm currently leaning towards the presentable and 
uncomplicated three-semicolon approach.



#lang racket/base
;; Copyright Neil Van Dyke.  See file "info.rkt".

(require (for-syntax racket/base
 syntax/parse)
 racket/match
 racket/system
 mcfly)

(module+ test
  (require overeasy))

(doc (require scribble/bnf
  "roomba-doc-utils.rkt"))

(doc (section "Introduction")

 (para "This package implements a "
   (hyperlink "http://docs.racket-lang.org/;
  "Racket")
   " interface for controlling many "
   (tech "models")
   " of the "
   (hyperlink "http://en.wikipedia.org/wiki/Roomba;
  "iRobot Roomba")
   " robot vacuum cleaner, and the "
   (hyperlink "http://www.irobot.com/create;
  "iRobot Create")

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] Re: single-file packages and embedded documentation and metadata

2017-11-10 Thread Jack Firth

>
> I have to redo my tools once more, and am sick of my current `(doc ...)` 
> and `@doc` format, *and the resulting dependency on McFly.* 
>

Could you elaborate on your experience with this? For a module with 
embedded docs I'd want to treat them the same as tests: in a special 
submodule which results in "build-time" dependencies instead of runtime 
dependencies. With the current package build system, users could install my 
package with embedded docs in pre-built form without installing build-time 
dependencies using `raco pkg install 
--catalog https://pkg-build.racket-lang.org/server/built/catalog/ --binary 
mypackage`, assuming they're running the same version of Racket as the 
package build server.

My gut instinct is that I'd like to explore making pre-built package 
installations more automatic and pain-free for package users, rather than 
asking package developers to avoid adding dependencies entirely by using 
special comments and separately installed special tools. Putting docs in 
specially-formatted comments means running my code in DrRacket won't check 
my docs for syntax errors.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] single-file packages and embedded documentation and metadata

2017-11-10 Thread Neil Van Dyke

Any comments on the following?

Since the Racket (PLT Scheme) version 1xx days, I have been doing 
embedded documentation in reusable Racket (and Scheme) modules, and have 
done a few iteration of tools for this.  I have to redo my tools once 
more, and am sick of my current `(doc ...)` and `@doc` format, and the 
resulting dependency on McFly.


For the next iteration, I'm coming back around to my ancient 
three-semicolon-Texinfo-based format, only using Scribble instead of 
Texinfo (though Markdown was tempting).  Like earlier, the programmer 
would usually only edit only one file per package, and the extra files 
like "info.rkt" and ".scrbl" would be generated automatically.


The questions I'm considering right now:

1. For encoded metadata, like the things traditionally found in 
"info.rkt" -- as well as for license/legal information, known issues, 
release history, and more detailed/flexible information about package 
dependencies -- should it use three-semicolon pseudo-Scribble forms, 
like in the example below, or should it do `(module+ info (define ...) 
(define ...) ...)`, with most of the small metadata in a `module+` at 
the top of the file, and probably the known issues and release history 
at the end of the file?  (A reason for the `info` submodule is that, 
although my tool has to parse all the three-semicolon stuff anyway, the 
info submodule is much better for other tools to get at the info, 
including, perhaps, someday, everyone supporting lightweight 
single-source-file modules for reusable Racket packages.)


2. Do we want to require the programmer to always repeat themselves 
things like procedure names and type info, like for `make-sandwich` in 
the example at the end of this file?  If not, do we want to also support 
something like make special pseudo-Scribble forms like `Procedure` that 
will be converted to `defproc` and populated with info from the code, 
like in `make-panini`?  Or do we want to also support something like 
Lisp docstrings, only in full Scribble, like in `make-wrap`?


Below is an example file.  You can also see a screenshot of what it 
looks like with some very simple editor support, to help visually 
differentiate "code" from docs/metadata (without getting philosophical 
any distinction), at: 
"http://www.neilvandyke.org/temporary/20171110-racket-example.png; (The 
syntax coloring in that screenshot is for my old Texinfo format, which 
happened to also have "@section"; more things would be colored with 
proper support for whatever the new format.)



#lang racket/base
;;; @Package{pasture}
;;; @Subtitle{Parallel Copying On Write}
;;; @Version{1.0}
;;; @Authors{John Smith}
;;; @Licenses{LGPLv3}
;;; @LegalNotices{
;;;   Pasture is Copyright 2025, Smithco, LLC, All rights reserved.
;;;   This program is Free Software; you can redistribute it and/or
;;;   modify it under the terms of the GNU Lesser General Public License
;;;   as published by the Free Software Foundation; either version 3 of
;;;   the License, or (at your option) any later version. This program
;;;   is distributed in the hope that it will be useful, but without any
;;;   warranty; without even the implied warranty of merchantability or
;;;   fitness for a particular purpose. See http://www.gnu.org/licenses/
;;;   for details.}

(require (for-syntax racket/base)
 foo
 bar)

;;; @section{Introduction}
;;;
;;; The Pasture package for Racket supports multiple COWs in parallel.
;;;
;;; It also involves sandwiches.

;;; @section{Sandwiches}
;;;
;;; A sandwich is a food typically consisting of vegetables, sliced
;;; cheese or meat, placed on or between slices of bread, or more
;;; generally any dish wherein two or more pieces of bread serve as a
;;; container or wrapper for another food type. The sandwich began as a
;;; portable finger food in the Western world, though over time it has
;;; become prevalent worldwide.

;;; @defproc[(make-sandwich (ingredients (listof ingredient?)))
;;; sandwich?]{`
;;;   Returns a sandwich given the right ingredients.
;;; }

(define/provide (make-sandwich ingredients)
  (vector 'sandwich
  (cons 'seitan ingredients)))

;;; @Procedure{
;;; Returns a panini given the right ingredients.
;;; }

(define/provide/contract make-panini
  (-> (ingredients (listof ingredient?)) panini?)
  (vector 'panini
  (cons 'portabello ingredients)))

(define/p/c/d make-wrap
  (-> (ingredients (listof ingredient?)) wrap?)
  @doc{Returns a wrap, given the right ingredients.}
  (vector 'wrap
  (cons 'sprouts ingredients)))

;;; @section{Anti-Tipping}

;;; Once activated, Pasture provides the following anti-tipping
;;; countermeasures automatically:
;;;
;;; @itemize{
;;; ...
;;; }

...

;;; @KnownIssues{
;;;
;;; @Issue{Needs support for pies.}
;;;
;;; @Issue[]{}
;;; }

;;; @History{
;;;
;;; @Version["1.0" "2025-12-25"]{
;;;
;;;   Added beeping.
;;;
;;;   @par

[racket-users] How does a macro properly expand to a define-runtime-path?

2017-11-10 Thread Alex Knauth
How does a macro properly expand to a define-runtime-path?

The documentation for define-runtime-path says this:

If the form has a source module according to syntax-source-module 
,
 then the source location is determined by preserving the original expression 
as a syntax object, extracting its source module path at run time (again using 
syntax-source-module 
),
 and then resolving the resulting module path index.

If the expression has no source module, the syntax-source 

 location associated with the form is used, if is a string or path.

If no source module is available, and syntax-source 

 produces no path, then current-load-relative-directory 

 is used if it is not #f. Finally, current-directory 

 is used if all else fails.


This tells me that if I want to specify the path it uses to anchor relative 
paths, I should give the define-runtime-path form a source location from the 
file I want it to use. 

(define-syntax-parser macro
  [(_ name path-expr)
   (quasisyntax/loc #'name
 (define-runtime-path name path-expr))])

However, this doesn't work. It finds paths relative to the macro's source file 
instead of the file that `name` came from.

What is the proper way to do this?

(Going through the steps from the documentation:
  * syntax-source-module returns #, which I assume it 
doesn't use.
  * syntax-source returns #, 
which I assume it should use.
I want it to use the use.rkt path as the anchor, but instead 
define-runtime-path is using the macro path. Why? What am I doing wrong?)

Alex Knauth

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] unexpected behavior of parametric polymorphism in Typed Racket

2017-11-10 Thread Sam Tobin-Hochstadt
Your intuitions about both questions are right here.

First, type variables are only introduced into scope automatically in
the function body for function definitions. You can use `#:forall`
with `define` to use polymorphic types inline, but that does not allow
you to define a polymorphic store value.

Second, you're right that subtyping allows `store` to be covariant,
meaning that anyone can put additional kinds of things in a store. You
can change this so that store is invariant by making the allowed
hashtables possible mutable, changing that field to `(HashTable addr
\alpha)`. Then your last example won't type check. However, that will
mean you need to pick an initial store type, instead of having it
inferred, and it will mean that a `(store Integer)` is not usable when
you need a `(store Number)`.

Sam

On Fri, Nov 10, 2017 at 11:10 AM, Richard Cobbe  wrote:
> I'm getting surprising results trying to define an abstract polymorphic
> type in Typed Racket.  Could someone shed some light on this?
>
> Consider the following definitions:
>
> (struct addr ([ptr : Natural]))
> (struct (α) store ([next-addr : addr]
>[table : (Immutable-HashTable addr α)]))
>
> (: empty-store (All (α) (-> (store α
> (define (empty-store)
>   (store (addr 0) (make-immutable-hash null)))
>
> (: alloc (All (α) (store α) α -> (Values (store α) addr)))
> (define (alloc st v)
>   (let ([a (store-next-addr st)])
> (values (store (inc-addr a) (hash-set (store-table st) a v))
> a)))
>
> ;; definitions of inc-addr & other store operations elided for brevity
>
> I have two questions at this point, a big one and a small one.
>
> Small question: it'd be nice to define empty-store to have type
> (All (α) (store α)) -- i.e., without the eta expansion -- but I can't
> figure out how to do this.  In particular, the following doesn't work:
>
> (: empty-store (All (α) (store α)))
> (define empty-store
>   (store (addr 0) ((inst make-immutable-hash addr α) null)))
>
> I get an error that "type name `α' is unbound", referring to the use of α
> in the use of `inst'.  This surprises me, because section 4.8.3 (Lexically
> Scoped Type Variables) of the Typed Racket Guide seems to say that α should
> be in scope here.  Does that only work for function definitions?
>
> Is there another way to do this without the thunk?
>
>  * * * * *
>
> Big question: with the original version of empty-store, I get some very
> surprising behavior.  Consider the following uses:
>
> (: s (store String))
> (define s ((inst empty-store String)))
>
> (define-values (s1 addr) (alloc s 3))
>
> This type-checks -- even the application of `alloc' in the last line.
> What's going on here?  In my understanding of parametric polymorphism, this
> can't be well-typed: α cannot be both String and Positive-Byte in the same
> type application.  Did I overlook something in the Typed Racket manuals
> that explains this?
>
> After the final definition, `s1' has type (store (U Positive-Byte String)).
> This does look sound to me, in that the values in the store are indeed
> either Positive-Bytes or Strings.  But this makes it difficult to use the
> type system to prevent clients from adding non-strings to the store.  Is it
> possible to express that restriction in Typed Racket?
>
> (Context: I only actually intend to use `store' with a single type, but I
> want to define that type in a separate module.  Since the type's definition
> refers to `addr', I made `store' polymorphic to break the cyclical
> dependency.)
>
> Thanks,
>
> Richard
>
> --
> You received this message because you are subscribed to the Google Groups 
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] switching to an alternate syntax using a lang extension: binding issues

2017-11-10 Thread Jens Axel Søgaard
fre. 10. nov. 2017 kl. 08.23 skrev Vincent Nys :

> Perhaps http://docs.racket-lang.org/multi-file-lang/index.html can be of
use?
-- 
-- 
Jens Axel Søgaard

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[racket-users] unexpected behavior of parametric polymorphism in Typed Racket

2017-11-10 Thread Richard Cobbe
I'm getting surprising results trying to define an abstract polymorphic
type in Typed Racket.  Could someone shed some light on this?

Consider the following definitions:

(struct addr ([ptr : Natural]))
(struct (α) store ([next-addr : addr]
   [table : (Immutable-HashTable addr α)]))

(: empty-store (All (α) (-> (store α
(define (empty-store)
  (store (addr 0) (make-immutable-hash null)))

(: alloc (All (α) (store α) α -> (Values (store α) addr)))
(define (alloc st v)
  (let ([a (store-next-addr st)])
(values (store (inc-addr a) (hash-set (store-table st) a v))
a)))

;; definitions of inc-addr & other store operations elided for brevity

I have two questions at this point, a big one and a small one.

Small question: it'd be nice to define empty-store to have type
(All (α) (store α)) -- i.e., without the eta expansion -- but I can't
figure out how to do this.  In particular, the following doesn't work:

(: empty-store (All (α) (store α)))
(define empty-store
  (store (addr 0) ((inst make-immutable-hash addr α) null)))

I get an error that "type name `α' is unbound", referring to the use of α
in the use of `inst'.  This surprises me, because section 4.8.3 (Lexically
Scoped Type Variables) of the Typed Racket Guide seems to say that α should
be in scope here.  Does that only work for function definitions?

Is there another way to do this without the thunk?

 * * * * *

Big question: with the original version of empty-store, I get some very
surprising behavior.  Consider the following uses:

(: s (store String))
(define s ((inst empty-store String)))

(define-values (s1 addr) (alloc s 3))

This type-checks -- even the application of `alloc' in the last line.
What's going on here?  In my understanding of parametric polymorphism, this
can't be well-typed: α cannot be both String and Positive-Byte in the same
type application.  Did I overlook something in the Typed Racket manuals
that explains this?

After the final definition, `s1' has type (store (U Positive-Byte String)).
This does look sound to me, in that the values in the store are indeed
either Positive-Bytes or Strings.  But this makes it difficult to use the
type system to prevent clients from adding non-strings to the store.  Is it
possible to express that restriction in Typed Racket?

(Context: I only actually intend to use `store' with a single type, but I
want to define that type in a separate module.  Since the type's definition
refers to `addr', I made `store' polymorphic to break the cyclical
dependency.)

Thanks,

Richard

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [racket-users] switching to an alternate syntax using a lang extension: binding issues

2017-11-10 Thread William G Hatch

On Thu, Nov 09, 2017 at 11:23:47PM -0800, Vincent Nys wrote:

Hi,

I have a domain-specific language with a non-parenthesis-based-syntax and
running programs written in this DSL sometimes leads to the creation of
some fairly large data structures. By "large" I mean that it is still
feasible to type them out, but an absolute drag. Doubly so when I have to
translate back and forth between the DSL syntax and Racket's syntax for the
data structures that are used internally. So I started working on a
language extension (called alpha-gamma) which allows me to switch to the
DSL syntax inside a #lang racket program by typing α(...) or γ(...), where
... is in non-Racket syntax.

I'm struggling a bit with the Racket reference, but by looking at some
other language extensions, I figured out how to do most of this with
make-meta-reader.  I don't understand how the whole process works, though,
and now I'm stuck with unbound identifiers in files using the extended
language.

Specifically, in a file called test.rkt written in #lang alpha-gamma racket
I have α(foo(bar)). The part between the outer parentheses is then parsed
as '(abstract-atom (abstract-atom-with-args "foo" "(" (abstract-term
(abstract-function-term "bar")) ")")), which is what I want. But the
bindings for abstract-atom, abstract-atom-with-args, abstract-term and
abstract-function-term are missing in test.rkt. I can get the value I want
by adding (require (only-in cclp/cclp-expander abstract-atom
abstract-atom-with-args abstract-term abstract-function-term)) at the top
of test.rkt, but I don't want to do that wherever I use the language
extension.

I guess these are the most important bits:

(define (wrap-reader reader)
 (define (rd . args) ; reader can read multiple datums at once...
   (parameterize ([current-readtable (make-αγ-readtable
(current-readtable))])
 (apply reader args)))
 rd)

(define-values (αγ-read αγ-read-syntax αγ-get-info)
 (make-meta-reader
  'αγ
  "language path"
  (λ (bstr)
(let* ([str (bytes->string/utf-8 bstr)]
   [sym (string->symbol str)])
  (and (module-path? sym)
   (vector
`(submod ,sym reader)
(string->symbol (string-append str "/lang/reader"))
  wrap-reader
  wrap-reader
  (lambda (proc) ; don't really care too much about get-info right now
(lambda (key defval)
  (define (fallback) (if proc (proc key defval) defval))
  (case key
[else (fallback)])

What I get from this is that my extended language still produces a module
like a regular language, but (require (only-in cclp/cclp-expander
abstract-atom abstract-atom-with-args abstract-term abstract-function-term))
should be spliced into this module somehow. Any suggestions on how to pull
that off?


To have those identifiers available to everything in the module
(including both inside the α language and the γ language) you can have
your #%module-begin add a require form to the basic #%module-begin
form it creates.  But for the binding to be visible, it needs to have
a subset of the scopes that identifiers used in the module will have.
To do that you can make the syntax objects for the identifiers
required using `datum->syntax`, using the syntax of the #%module-begin
(or in this case just #f would make them visible to the whole module).

I would also recommend looking at two projects that attack similar
problems.  One is my Rash package[1], where I have a macro called
`rash` that lets you embed code in a particular non-s-expression
syntax inside normal Racket (and also allows escaping back into racket
and nesting arbitrarily deeply).  You can use it to write things like
this:

(let* ([project-dir "my-racket-project"]
  [rktfiles (rash «cd $project-dir
   ls | grep rkt |> string-split _ "\n"»)])
 ...)

The key thing that I think you might find appealing about rash's
method of embedding is that rather than trying to have one reader at
the top level that can read multiple languages, it instead delimits
sections of code that a macro can apply a different reader to during
expansion time.

The other project you should look at is Michael Ballantyne's
multiscope package[2].  It essentially lets you switch back and forth
between two different namespaces within one module.  This could let
you eg. have different bindings visible inside the α and γ blocks (if
eg. α and γ use the same name for semantically different things).

[1] https://github.com/willghatch/racket-rash
[2] https://github.com/michaelballantyne/multiscope

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.