These are extracted from discussions I triggered on the Racket Slack. Thanks to everyone who participated in the discussion threads! :-)
Use (and value #t)
to convert a value to its boolean equivalent.
To enter a module in the REPL to access the non-provide
d symbols:
,enter my-module
If you have defined a test module inside my-module
with
(module test+
...)
you can enter it with
,enter (submod my-module test)
To access individual identifiers, you can use
require/expose
from the module rackunit
.
I wanted to create a struct similar to
(struct foo (x y))
with accessors foo-x
and foo-y
without also defining the name
foo
(because I wanted the name for a mapping from symbol to
accessor, say, 'x
-> foo-x
).
The obvious
(struct foo (x y) #:constructor-name unused-foo)
doesn't work as intended because the name foo
is still created for
pattern matching transformers to be used at compile time.
Instead of the above code, use
(struct foo (x y) #:name unused-foo)
Another approach is
(struct foo (x y) #:omit-define-syntaxes)
For many command line programs, the interpreter startup time can be much longer than the actual processing in the program. The startup time for
#lang racket/base
"Hello world"
executed with racket hello.rkt
on my computer is 145 ms. Compilation
with raco exe
or raco make
reduces the startup time to about 130
ms.
To reduce startup time, see Fast Racket. However, this doesn't bring the startup time below the mentioned 130 ms.
Most of the interpreter startup time is from loading "linklets". The command
$ PLT_LINKLET_TIMES=1 racket -l racket
shows how Racket spends the time when loading linklets. An example output is
;; on-demand 0 [0] ms ; 21 times
;; read-linklet 12 [3] ms ; 283 times
;; run 22 [5] ms
;; instantiate 22 [5] ms ; 1255 times
;; read 51 [0] ms
;; read-bundle 0 [0] ms
;; faslin-literals 14 [0] ms ; 1216 times
;; faslin-code 37 [0] ms ; 1216 times
;; boot 73 [0] ms
;; total 158 [8] ms
;;
;; faslin-code 5 MB
"faslin" means "fasl in", see Fast Load Serialization.
On some occasions, I didn't get proper stacktraces when running
$ raco exe file-to-test.rkt
i. e. in some cases the stacktrace didn't include the frames where the failure (say, a contract violation) actually occurred.
One (somewhat verbose) approach is
$ racket -l racket/init -l errortrace -e '(require (submod "file.rkt" test))'
Another approach is to use the test-e
command, provided by
this package.
I thought that you could add a contract to a struct
constructor like
to any regular function. For example, in one file I had
(provide
(contract-out
[sort-spec (-> symbol? symbol? sort-spec?)]
...)
sort-spec?
value-sort-func)
However, this resulted in an error:
task-group.rkt:20:24: struct: parent struct type not defined;
identifier does not name struct type information
at: sort-spec
in: (struct task-group-spec sort-spec (tag-key) #:transparent)
context...:
/.../collects/racket/private/define-struct.rkt:153:2
/.../collects/syntax/wrap-modbeg.rkt:46:4
There are different workarounds:
- Use
struct/contract
, but this doesn't have all customizations thatstruct
has. Another downside is that the contract affects the whole module, not just the module boundary, so this approach may come with a performance degradation. - Provide the contract with the
#:guard
keyword argument forstruct
. As before, this condition is enforced for all instances, whether in the defining module or not. - Use
[struct my-struct ...]
insidecontract-out
, see Contracts on structures. This applies only to the module boundary, but it implies that all "functions" of the struct are exported, in particular the accessors. This is a problem if such accessors should be kept private. - Not using a contract for the struct construction at all.
There's no common idiom for this, but my approach my-name/private
was considered good. :-)
In a module, I have
(with-handlers ([exn:fail? (lambda (exn) (eprintf "~a\n" (exn-message exn)))])
(main))
An error message from that might look like
open-input-file: cannot open input file
path: /home/.../todoreport/crd,desc
system error: No such file or directory; errno=2
However, if the message is intended for end users, the function name isn't relevant.
It seems that the caught exception object doesn't have fields for the error number, the error reason etc., only the message itself. So the only workaround is using text operations to strip the "function-name: " from the error message.
An alternative to with-handlers
is setting the
error-display-handler
parameter. This example also contains the code
to strip the function name:
(let ([this-handler (error-display-handler)])
(error-display-handler
(lambda (msg exn)
(this-handler
(regexp-replace #px"^.*?: " msg "")
exn))))
Put the code for the respective platform in its own module and use
dynamic-require
. To actually distinguish the platforms, use
system-type
.
To wrap a paragraph of text like Python's textwrap
module, use
wrap-line
from
scribble/text/wrap
.
This function isn't documented in Scribble form, but well-documented
in the code. At least it's not in a private
directory that would
clearly suggest not using the function.
As an alternative, wrap the text "manually". This sounds odd, but I asked for using the function to format command line help, and it turned out it was better to insert the line breaks manually anyway.
Package management in Racket has a lot of information on packaging, but it's easy to miss the forest for the trees.
Also raco setup
is useful for some things.
There's a tutorial for creating single-collection packages.
See the lazytree package for an example of a multi-collection package.
$ raco pkg new <your-package-name>
creates a skeleton directory structure (with a few files) for a new
package your-package-name
.
(not raco pkg setup
)
Before using raco setup
, the package has to be installed.
$ raco setup --fix-pkg-deps --pkgs <your-package-name>`
adds missing dependencies to the info.rkt
file.
raco setup
can also be used with the module name/path, say:
$ raco setup file/todo-txt
A package can be reinstalled by using
$ raco pkg remove <package-name>
and
$ raco pkg install ../<working-dir>
but it's shorter to use
$ raco setup --check-pkg-deps --pkgs <package-name>
once the package has been initially installed.
Many possibilities of the info.rkt
file(s) are documented in
Controlling raco setup with "info.rkt"
Files
and "info.rkt" file
format.
Installing binaries is possible with racket-launcher-names
and
racket-launcher-libraries
in the info.rkt
. Example:
(define racket-launcher-names '("todoreport"))
(define racket-launcher-libraries '("todoreport.rkt"))
where the paths in racket-launcher-libraries
is relative to the
info.rkt
file the forms appear in. It's not possible to use relative
paths that go up in the directory hierarchy, say,
"../todoreport.rkt"
.
You can have different info.rkt
files in the directory tree for the
package. Here's an example of a package of mine:
$ tree
├── file
│ ├── info.rkt [1]
│ ├── todoreport
│ │ ├── info.rkt [2]
│ │ └── private
│ │ └── cli.rkt
│ ├── todoreport.rkt
│ ├── todo-txt
│ │ ├── doc
│ │ │ ├── ...
│ │ │ └── todo-txt
│ │ │ └── ...
│ │ ├── info.rkt [3]
│ │ ├── private
│ │ │ ├── task-group.rkt
│ │ │ └── task.rkt
│ │ └── scribblings
│ │ └── todo-txt.scrbl
│ └── todo-txt.rkt
├── info.rkt [4]
├── LICENSE
└── ...
As you can see, there are four info.rkt
files:
- This contains the launcher definition as shown above.
- This only contains
(define collection "todoreport")
- This is similar to 2, but also contains a definition for the
documentation under
scribblings
. - This file contains most of the package definition, in particular
the line
(define collection 'multi)
, the dependencies, the build dependencies, a package description and the package version. This file is adapted from the file that is generated byraco pkg new
.
$ raco docs <your-module>
for example
$ raco docs file/todo-txt
opens the generated documentation for an already installed package.
The "official" way to achieve this is to add information for
Scribble
to the contract-out
section of module provide
s. Here's a blog
article.
Bogdan Popa created a module that should be able to extract the contract information without changes to the module where the functions or structs are defined.