
* The main page's title appears in search results, so use "The Racket Language" instead of "Racket". Additional minor tweaks to a few more titles to make them presentable as search results. * Add a `#:description' keyword to make it easy to add it to pages. Add such texts to the main page, downloads, community, learning, people, and mailing list pages. * Add a <meta name="robots" content="NOODP" /> to the front page, to avoid getting the ODP (dmoz) blurb, hopefully the new and improved description(s) will be used instead. (If not, this should be reverted.)
538 lines
22 KiB
Racket
538 lines
22 KiB
Racket
#lang meta/web
|
||
|
||
(require "resources.rkt" "code.rkt" "download.rkt" "learning.rkt")
|
||
|
||
(define (doc path . text)
|
||
(apply a href: (list "http://docs.racket-lang.org/" path) text))
|
||
|
||
(struct example (code desc))
|
||
|
||
(define ((example-with-help . help) code desc) (example code (list desc help)))
|
||
(define generic-example
|
||
@example-with-help{
|
||
@p{To run the example, install Racket, start DrRacket, paste the example
|
||
program into the top area in DrRacket, and click the Run button.
|
||
Alternatively, save the program to a file and run @tt{racket} on the
|
||
file.}})
|
||
(define cmdline-example
|
||
@example-with-help{
|
||
@p{This example is a command-line script. To run the example, install
|
||
Racket, paste the example program into a file, and run @tt{racket} on
|
||
the file with command-line arguments after the filename. Alternatively,
|
||
for a Unix installation, you can add @tt{#!/usr/bin/env racket} at the
|
||
top and make the file executable, and then you can run the file
|
||
directly.}})
|
||
(define scribble-example
|
||
@example-with-help{
|
||
@p{To run the example, install Racket, start DrRacket, and paste the
|
||
example program into the top area in DrRacket. When a program in a
|
||
Scribble language is opened in DrRacket, a @b{Scribble HTML} button
|
||
appears for rendering the document to HTML. Click it.}})
|
||
(define graphical-example
|
||
@example-with-help{
|
||
@p{To run the example, install Racket, start DrRacket, paste the example
|
||
program into the top area in DrRacket, and click the Run button.}})
|
||
|
||
(define desc div)
|
||
(define (elemcode . strs) (apply tt strs))
|
||
|
||
(define examples
|
||
@; --- Each example here should at most 7 lines long ---------------
|
||
(list
|
||
@; Candidates for initial example: --------------------------------
|
||
(list
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket
|
||
;; Finds Racket sources in all subdirs
|
||
(for ([path (in-directory)])
|
||
(when (regexp-match? #rx"[.]rkt$" path)
|
||
(printf "source file: ~a\n" path)))}
|
||
@desc{The @elemcode{in-directory} function constructs a sequence that
|
||
walks a directory tree (starting with the current directory, by default)
|
||
and generates paths in the tree. The @elemcode{for} form binds
|
||
@elemcode{p} to each path in the sequence, and @elemcode{regexp-match?}
|
||
applies a pattern to the path.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang web-server/insta
|
||
;; A "hello world" web server
|
||
(define (start request)
|
||
(response/xexpr
|
||
'(html
|
||
(body "Hello World"))))}
|
||
@desc{This example implements a web server using the
|
||
@elemcode{web-server/insta} language. Each time a connection is made to
|
||
the server, the @elemcode{start} function is called to get the HTML to
|
||
send back to the client.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket ; An echo server
|
||
(define listener (tcp-listen 12345))
|
||
(let echo-server ()
|
||
(define-values (in out) (tcp-accept listener))
|
||
(thread (lambda () (copy-port in out)
|
||
(close-output-port out)))
|
||
(echo-server))}
|
||
@desc{Racket makes it easy to use TCP sockets and spawn threads to handle
|
||
them. This program starts a server at TCP port 12345 that echos
|
||
anything a client sends back to the client.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket
|
||
;; Report each unique line from stdin
|
||
(let ([saw (make-hash)])
|
||
(for ([line (in-lines)])
|
||
(unless (hash-ref saw line #f)
|
||
(displayln line))
|
||
(hash-set! saw line #t)))}
|
||
@desc{Uses a hash table to record previously seen lines. You can run this
|
||
program in DrRacket, but it makes more sense from the command line.}))
|
||
@; Additional examples: -------------------------------------------
|
||
(list
|
||
(graphical-example ; ---------------------------------------------
|
||
@code{#lang racket ; A picture
|
||
(require 2htdp/image)
|
||
(let sierpinski ([n 8])
|
||
(if (zero? n)
|
||
(triangle 2 'solid 'red)
|
||
(let ([t (sierpinski (- n 1))])
|
||
(freeze (above t (beside t t))))))}
|
||
@desc{The @elemcode{2htdp/image} library provides easy-to-use functions
|
||
for constructing images, and DrRacket can display an image result as
|
||
easily as it can display a number result. In this case, a
|
||
@elemcode{sierpinski} function is defined and called (at the same time)
|
||
to generate a Sierpinski triangle of depth 8.})
|
||
(graphical-example ; ---------------------------------------------
|
||
@code{#lang racket/gui ; A GUI guessing game
|
||
(define f (new frame% [label "Guess"]))
|
||
(define n (random 5)) (send f show #t)
|
||
(define ((check i) btn evt)
|
||
(message-box "." (if (= i n) "Yes" "No")))
|
||
(for ([i (in-range 5)])
|
||
(make-object button% (format "~a" i) f (check i)))}
|
||
@desc{This simple guesing game demonstates Racket's class-based GUI
|
||
toolkit. The @elemcode{frame%} class implements a top-level window, and
|
||
@elemcode{button%} obviously implements a button. The @elemcode{check}
|
||
function defined here produces an function that is used for the button's
|
||
callback action.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket ; Simple web scraper
|
||
(require net/url net/uri-codec)
|
||
(define (let-me-google-that-for-you str)
|
||
(let* ([g "http://www.google.com/search?q="]
|
||
[u (string-append g (uri-encode str))]
|
||
[rx #rx"(?<=<h3 class=\"r\">).*?(?=</h3>)"])
|
||
(regexp-match* rx (get-pure-port (string->url u)))))}
|
||
@desc{Add a call to @elemcode{let-me-google-that-for-you} to get a list of
|
||
search results.})
|
||
(cmdline-example ; -----------------------------------------------
|
||
@code{#lang racket
|
||
;; A dice-rolling command-line utility
|
||
(command-line
|
||
#:args (dice sides)
|
||
(for ([i (in-range (string->number dice))])
|
||
(displayln
|
||
(+ 1 (random (string->number sides))))))}
|
||
@desc{Playing a game but no dice on hand? Let Racket roll for you. The
|
||
@elemcode{command-line} form makes sure that the right number of
|
||
arguments are provided and automatically implements the @tt{--help}
|
||
switch.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket
|
||
;; Print the Greek alphabet
|
||
(for ([i (in-range 25)])
|
||
(displayln
|
||
(integer->char
|
||
(+ i (char->integer #\u3B1)))))}
|
||
@desc{The only reason we use the encoded form of a character
|
||
@elemcode{#\u3B1} instead of the more direct form @elemcode{#\α} is that
|
||
we don't trust your browser to render it correctly. DrRacket is
|
||
perfectly happy with @elemcode{#\α}.})
|
||
(graphical-example ; ---------------------------------------------
|
||
@code{#lang htdp/bsl ; Any key inflates the balloon
|
||
(require 2htdp/image) (require 2htdp/universe)
|
||
(define (balloon b) (circle b "solid" "red"))
|
||
(define (blow-up b k) (+ b 5))
|
||
(define (deflate b) (max (- b 1) 1))
|
||
(big-bang 50 (on-key blow-up) (on-tick deflate)
|
||
(to-draw balloon 200 200))}
|
||
@desc{Racket's mission includes education at all levels. This program
|
||
uses the @elemcode{htdp/bsl} teaching language, the
|
||
@elemcode{2htdp/image} library for creating pictures in the teaching
|
||
languages, and the @elemcode{2htdp/universe} library for interactive
|
||
animations.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang lazy
|
||
;; An infinite list:
|
||
(define fibs
|
||
(list* 1 1 (map + fibs (cdr fibs))))
|
||
@||
|
||
;; Print the 1000th Fibonacci number:
|
||
(print (list-ref fibs 1000))}
|
||
@desc{And now for something completely different. The @elemcode{lazy}
|
||
language is more like Haskell than Lisp, so feel free to build an
|
||
infinite list and look at only part of it.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang typed/racket
|
||
;; Using higher-order occurrence typing
|
||
(define-type SrN (U String Number))
|
||
(: tog ((Listof SrN) -> String))
|
||
(define (tog l)
|
||
(apply string-append (filter string? l)))
|
||
(tog (list 5 "hello " 1/2 "world" (sqrt -1)))}
|
||
@desc{Racket's type system is designed to let you add types after you've
|
||
worked for a while in untyped mode — even if your untyped program
|
||
wouldn't fit nicely in a conventional type system.})
|
||
(scribble-example ; ----------------------------------------------
|
||
@code|{#lang scribble/base
|
||
@; Generate a PDF or HTML document
|
||
@title{Bottles --- @italic{Abridged}}
|
||
@(apply itemlist
|
||
(for/list ([n (in-range 100 0 -1)])
|
||
@item{@(format "~a" n) bottles.}))}|
|
||
@desc{This program uses the @elemcode{scribble/base} language for
|
||
generating documents using a prose-friendly syntax.})
|
||
(graphical-example ; ---------------------------------------------
|
||
@code{#lang racket ; draw a graph of cos
|
||
(require plot) ; and deriv^3(cos)
|
||
(define ((deriv f) x)
|
||
(/ (- (f x) (f (- x 0.001))) 0.001))
|
||
(define (thrice f) (lambda (x) (f (f (f x)))))
|
||
(plot (list (function ((thrice deriv) sin) -5 5)
|
||
(function cos -5 5 #:color 'blue)))}
|
||
@desc{This program uses the @elemcode{plot} library to draw plots of
|
||
functions. Note that the plots are actual value, which DrRacket shows
|
||
in graphical form.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket ; Sending email from racket
|
||
(require net/sendmail)
|
||
(sleep (* (- (* 60 4) 15) 60)) ; 4h - 15m
|
||
(send-mail-message
|
||
(getenv "EMAIL") "Parking meter alert!"
|
||
(list (getenv "EMAIL")) null null
|
||
'("Time to go out and move your car."))}
|
||
@desc{Racket comes with plenty of libraries.})
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang scheme/base ; Simple use of the FFI
|
||
(require ffi/unsafe)
|
||
(define mci-send-string
|
||
(get-ffi-obj "mciSendStringA" "Winmm"
|
||
(_fun _string [_pointer = #f] [_int = 0]
|
||
[_pointer = #f] -> [ret : _int])))
|
||
(mci-send-string "play sound.wav wait")}
|
||
@desc{Using the FFI means that you're not limited to using Racket
|
||
libraries: pulling out a foreign function is easy, and can even be done
|
||
dynamically on the REPL.})
|
||
;; Is this effective without any highlights?
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang datalog
|
||
ancestor(A, B) :- parent(A, B).
|
||
ancestor(A, B) :-
|
||
parent(A, C), D = C, ancestor(D, B).
|
||
parent(john, douglas).
|
||
parent(bob, john).
|
||
ancestor(A, B)?}
|
||
@desc{Racket is useful for building other languages. This example uses
|
||
the pre-packaged Racket implementation of Datalog, a logic programming
|
||
language. If you use this from DrRacket, you'll see that it provides
|
||
proper highlighting, Check Syntax, and a Datalog-specific REPL.})
|
||
#; ; Not easy to present something like this.
|
||
(generic-example ; -----------------------------------------------
|
||
@code{#lang racket
|
||
(provide (except-out (all-from-out racket)
|
||
#%top #%app)
|
||
(rename-out [top #%top] [app #%app]))
|
||
(define-syntax-rule (top . x) 'x)
|
||
(define-syntax-rule (app f . xs)
|
||
(if (hash? f) (hash-ref f . xs) (f . xs)))
|
||
}
|
||
@desc{TODO, and use this example:
|
||
@pre{#lang s-exp "foo"
|
||
(define x (make-hasheq))
|
||
(hash-set! x A B)
|
||
(x A)}})
|
||
)))
|
||
|
||
(define blurb "Racket is a programming language")
|
||
|
||
(provide set-news-flashes!)
|
||
(define news-flashes #f)
|
||
(define (set-news-flashes! . text)
|
||
(when news-flashes (error 'set-news-flashes! "text already set"))
|
||
(set! news-flashes text))
|
||
|
||
(provide index)
|
||
(define index
|
||
(page #:link-title "About" #:window-title "The Racket Language"
|
||
#:description
|
||
@'{Racket is a modern programming language in the Lisp/Scheme family, @;
|
||
suitable for a wide range of applications. @;
|
||
Racket provides a rich language extension API, the DrRacket @;
|
||
integrated development environment, and many batteries-included @;
|
||
libraries.}
|
||
#:extra-headers @list{@meta[name: "robots" content: "NOODP"]
|
||
@(lazy more.css)}
|
||
@div[class: 'leftpane]{
|
||
@span[style: "font-size: large; font-weight: bold;"]{Racket}
|
||
is a programming language.
|
||
@(and news-flashes (list br (div style: "width: 100%;" news-flashes)))}
|
||
@div[class: 'downloadbutton]{@download-button}
|
||
@div[class: 'aboutpane]{
|
||
@div[class: 'panetitle]{Start Quickly}
|
||
@div{@(apply slideshow-panel examples)}
|
||
@p{@doc["quick/"]{Draw more pictures} or
|
||
@doc["more/"]{build a web server from scratch}. Racket includes both
|
||
@doc[""]{batteries} and a @doc["drracket/"]{programming environment},
|
||
so @doc["getting-started/"]{get started}!}}
|
||
@((λ xs (table class: 'threepanes
|
||
(tr (map (λ (x) (td (div class: 'panetitle (car x)) (cdr x)))
|
||
xs))))
|
||
(list "Grow your Program"
|
||
@p{Racket's
|
||
@doc["guide/intro.html#(part._.Interacting_with_.Racket)"]{
|
||
interactive mode}
|
||
encourages experimentation, and quick scripts easily compose into
|
||
larger systems. Small scripts and large systems both benefit from
|
||
@doc["guide/performance.html"]{native-code JIT compilation}.
|
||
When a system gets too big to keep in your head, you can add
|
||
@doc["ts-guide/index.html"]{static types}.})
|
||
(list "Grow your Language"
|
||
@p{@doc["guide/languages.html"]{Extend Racket} whenever you need to.
|
||
Mold it to better suit your tasks without sacrificing
|
||
@doc["guide/dialects.html"]{interoperability} with existing
|
||
libraries and without having to modify the
|
||
@doc["guide/intro.html"]{tool chain}. When less is more, you can
|
||
remove parts of a language or start over and build a new one.})
|
||
(list "Grow your Skills"
|
||
@p{Whether you're just @-htdp{starting out}, want to know more about
|
||
programming language @-plai{applications} or @-redex{models},
|
||
looking to @continue{expand your horizons}, or ready to dive into
|
||
@learning{research}, Racket can help you become a better programmer
|
||
and system builder.}))))
|
||
|
||
(define (slideshow-panel l1 l2)
|
||
(define l (append l1 l2))
|
||
(define button-ids+labels '())
|
||
;; this separator is shown in non-CSS browsers (eg, textual ones)
|
||
(define invisible-separator @div[style: "display: none;"]{@br{}@hr{}})
|
||
(define (button txt tip id onclick)
|
||
(set! button-ids+labels (cons (cons id txt) button-ids+labels))
|
||
(a href: "#" id: id onclick: (list onclick "; return false;") title: tip
|
||
nbsp)) ; empty, filled by JS code, so JS-less browsers won't see it
|
||
(div class: 'slideshow
|
||
(div class: 'buttonpanel
|
||
@button["<" "Previous example" 'rewindbutton "rewind_show()"]
|
||
@button[">" "Next example" 'advancebutton "advance_show()"]
|
||
@button["?" "Explain this code" 'helpbutton "set_help(!help_showing)"]
|
||
(div class: 'hiddenhelp id: "helppanel" style: "display: none;"
|
||
(div class: 'helpcontent
|
||
@button["close" "Close help" 'closebutton "set_help(false)"]
|
||
(div id: 'helpdesc "") ; placeholder for the descriptions (see below)
|
||
@div[class: 'helptext]{
|
||
Form and function names in the code are hyperlinked to
|
||
documentation, so click on them for more information.})))
|
||
(for/list ([elem (in-list l)] [pos (in-naturals)])
|
||
@list{
|
||
@invisible-separator
|
||
@pre[class: 'slideshowframe id: @list{frame@pos}
|
||
style: @list{display: @(if (zero? pos) "block" "none")@";"}]{
|
||
@(example-code elem)}@;
|
||
@; have the descriptions appear in a logical place and then ...
|
||
@div[id: @list{helpframe@pos} style: "display: none;"]{
|
||
@(example-desc elem)}})
|
||
@invisible-separator
|
||
@script/inline[type: "text/javascript"]{
|
||
@; ... move them to a convenient-for-display place
|
||
var helpdesc = document.getElementById("helpdesc");
|
||
for (var i=0@";" i<@(length l)@";" i++) {
|
||
var help_item = document.getElementById("helpframe"+i);
|
||
help_item.parentNode.removeChild(help_item);
|
||
helpdesc.appendChild(help_item);
|
||
}
|
||
var showing = 0, help_showing = false;
|
||
var frame_s=new Array(), helpframe_s=new Array();
|
||
for (var i=0@";" i<@(length l)@";" i++) {
|
||
frame_s[i] = document.getElementById("frame" + i).style;
|
||
helpframe_s[i] = document.getElementById("helpframe" + i).style;
|
||
}
|
||
var advbutton_s = document.getElementById("advancebutton").style;
|
||
var rewbutton_s = document.getElementById("rewindbutton").style;
|
||
function set_display(disp) {
|
||
frame_s[showing].display = disp;
|
||
helpframe_s[showing].display = disp;
|
||
}
|
||
function change_show_to(new_showing) {
|
||
set_display("none");
|
||
showing = new_showing;
|
||
set_display("block");
|
||
rewbutton_s.color = (showing==0) ? "#aaaaaa" : "#444444";
|
||
advbutton_s.color =
|
||
(showing==@(sub1 (length l))) ? "#aaaaaa" : "#444444";
|
||
}
|
||
function advance_show() {
|
||
if (showing < @(sub1 (length l))) change_show_to(showing+1);
|
||
}
|
||
function rewind_show() {
|
||
if (showing > 0) change_show_to(showing-1);
|
||
}
|
||
var help_panel_s = document.getElementById("helppanel").style;
|
||
function set_help(show) {
|
||
help_panel_s.display = show ? "block" : "none";
|
||
help_showing = show;
|
||
}
|
||
change_show_to(Math.floor(Math.random() * @(length l1)));
|
||
@; display button texts now, instead of making it part of the html,
|
||
@; so it's not shown on JS-less browsers
|
||
@(add-newlines
|
||
(for/list ([id+label (in-list button-ids+labels)])
|
||
(let ([id (car id+label)] [label (cdr id+label)])
|
||
@list{document.getElementById("@id").textContent = "@label"@";"})))
|
||
}))
|
||
|
||
;; TODO
|
||
;; (define screenshots
|
||
;; (let ([image (copyfile (in-here "screenshot.jpg"))])
|
||
;; @a[href: screenshots]{
|
||
;; @img[src: image alt: "[screenshots]" border: 0
|
||
;; style: "margin-bottom: 2px;"]@;
|
||
;; @|br|@small{Screenshots}}))
|
||
|
||
;; (define tour-video
|
||
;; (page #:title "DrRacket Tour" #:file "tour.html"
|
||
;; (define (center . body)
|
||
;; (table align: 'center style: "margin: 3em 0em;"
|
||
;; (tr (td align: 'center body))))
|
||
;; ;; someone posted a comment saying that adding "&fmt=18" to the url
|
||
;; ;; shows a higher resolution video, but it looks exactly the same.
|
||
;; (define url "http://www.youtube.com/v/vgQO_kHl39g&hl=en")
|
||
;; @center{
|
||
;; @object[type: "application/x-shockwave-flash" data: url
|
||
;; width: (round (* 3/2 425)) height: (round (* 3/2 344))]{
|
||
;; @param[name: "movie" value: url]}}))
|
||
|
||
|
||
;; resources that are specific to the front page
|
||
|
||
(define loud (copyfile (in-here "loud.png")))
|
||
|
||
(define more.css
|
||
@plain[#:referrer (λ (url) (link rel: "stylesheet" type: "text/css"
|
||
href: url title: "default"))]{
|
||
.bodycontent {
|
||
background-image: url('@loud');
|
||
background-repeat: no-repeat;
|
||
background-position: center top;
|
||
}
|
||
.leftpane {
|
||
font-size: medium;
|
||
float: left;
|
||
width: 20%;
|
||
}
|
||
.aboutpane {
|
||
width: 56%;
|
||
margin-right: auto;
|
||
margin-left: auto;
|
||
}
|
||
.panetitle {
|
||
width: 100%;
|
||
font-size: large;
|
||
font-weight: bold;
|
||
color: #dd0000;
|
||
}
|
||
.threepanes {
|
||
width: 100%;
|
||
}
|
||
.threepanes td {
|
||
vertical-align: top;
|
||
margin: auto;
|
||
width: 30%;
|
||
padding: 0.5em;
|
||
}
|
||
.slideshow {
|
||
position: relative;
|
||
}
|
||
.slideshowframe {
|
||
height: 17ex;
|
||
}
|
||
.buttonpanel {
|
||
display: block;
|
||
position: absolute;
|
||
left: 100%;
|
||
top: -3ex;
|
||
width: 3em;
|
||
margin: 0em 0em 0em -5em;
|
||
}
|
||
#advancebutton, #rewindbutton, #helpbutton, #closebutton {
|
||
text-decoration: none;
|
||
border: 1px solid #ddddd;
|
||
font-weight: bold;
|
||
color: #44444;
|
||
background-color: #eeeee;
|
||
padding: 0px 1px 0px 1px;
|
||
}
|
||
#advancebutton, #rewindbutton {
|
||
margin: 0px 1px 0px 1px;
|
||
}
|
||
#helpbutton {
|
||
margin: 0px 1px 0px 10px;
|
||
}
|
||
.hiddenhelp {
|
||
width: 0em;
|
||
margin-left: 2em;
|
||
}
|
||
.helpcontent {
|
||
width: 20em;
|
||
background-color: #ffffee;
|
||
padding: 10px;
|
||
margin-top: 3px;
|
||
border: 1px solid black;
|
||
}
|
||
#closebutton {
|
||
font-size: small;
|
||
margin-bottom: 1em;
|
||
}
|
||
.helptext, #helpdesc {
|
||
margin-top: 0.5em;
|
||
}
|
||
.helptext {
|
||
font-size: small;
|
||
}
|
||
.downloadbutton {
|
||
position: relative;
|
||
float: right;
|
||
}
|
||
@;
|
||
.codecomment {
|
||
color: #c2741f;
|
||
}
|
||
.codeparenthesis {
|
||
color: #843c24;
|
||
}
|
||
.codeconstant, .codestring {
|
||
color: #228b22;
|
||
}
|
||
.codeid, .codemodpath {
|
||
color: #262680;
|
||
}
|
||
.codeimportid {
|
||
color: blue;
|
||
}
|
||
.codeimportform {
|
||
font-weight: bold;
|
||
}
|
||
.codelinkimportid {
|
||
color: blue;
|
||
text-decoration: none;
|
||
}
|
||
.codelinkimportform {
|
||
font-weight: bold;
|
||
color: black;
|
||
text-decoration: none;
|
||
}
|
||
.codelinkimportid:hover {
|
||
text-decoration: none;
|
||
}
|
||
.codelinkimportform:hover {
|
||
text-decoration: none;
|
||
}
|
||
.codemodpath:hover {
|
||
text-decoration: none;
|
||
}})
|