Files
js-maker/demo/js-usecases.rkt
T

305 lines
10 KiB
Racket

#lang racket/base
(require racket/list
"../main.rkt")
(provide usecase-random-number
usecase-unique-values
usecase-falsey-values
usecase-currying
usecase-object-destructuring
usecase-timer-interval
usecase-object-props
usecase-string-concat-order
usecase-freeze-vs-seal
usecase-switch
usecase-class-constructor
usecase-sort-objects-by-property
usecase-delete-array-elements
usecase-bubble-sort
usecase-binary-search
usecase-map-count-occurrences
usecase-get-html-three-ways
usecase-anagram
usecase-pairs-equal-target
usecase-fetch-api
all-js-usecases
show-js-usecases
write-js-usecases-file)
;; Use case 01: generate a random integer between 1 and 5.
(define usecase-random-number
(js
(define (randomBetween1And5)
(return (+ (send Math floor (* (send Math random) 5)) 1)))))
;; Use case 02: get unique values from an array with duplicates using Set.
(define usecase-unique-values
(js
(define (uniqueValues xs)
(return (send Array from (new Set xs))))))
;; Use case 03: the six JavaScript falsey values.
(define usecase-falsey-values
(js
(define (falseyValues)
(return (array #f 0 "" js-null js-undefined js-NaN)))))
;; Use case 04: currying, simple example.
(define usecase-currying
(js
(define (add x)
(return (lambda (y)
(return (+ x y)))))))
;; Use case 05: object destructuring.
(define usecase-object-destructuring
(js
(define (describePerson person)
(let-object ([name 'name]
[age 'age 0])
person
(return (string-append name ":" (number->string age)))))))
;; Use case 06: get out of a timer interval with setInterval/clearInterval.
(define usecase-timer-interval
(js
(define (startTimer)
(let* ([ticks 0]
[intervalId #f])
(set! intervalId
(setInterval (lambda ()
(set! ticks (+ ticks 1))
(when (= ticks 3)
(clearInterval intervalId)))
10))
(return (object 'id intervalId
'getTicks (lambda () (return ticks))))))))
;; Use case 07: get/set/delete object properties. The value of a is read via
;; dot access, bracket access, and destructuring.
(define usecase-object-props
(js
(define (objectProps)
(let* ([obj (object 'a 1)]
[a1 obj.a]
[a2 (js-ref obj "a")])
(let-object ([a3 'a]) obj
(set! obj.b 2)
(set-prop! obj "c" 3)
(delete-prop! obj "a")
(return (array a1 a2 a3 obj.b (js-ref obj "c")
(send Object hasOwn obj "a"))))))))
;; Use case 08: string concatenation; order matters with JavaScript +.
(define usecase-string-concat-order
(js
(define (concatOrder)
(return (array (+ 1 2 "3")
(+ "1" 2 3))))))
;; Use case 09: Object.freeze() vs Object.seal().
(define usecase-freeze-vs-seal
(js
(define (freezeVsSeal)
(let* ([frozen (send Object freeze (object 'a 1))]
[sealed (send Object seal (object 'a 1))])
(set! frozen.a 9)
(set! sealed.a 9)
(delete-prop! sealed "a")
(return (array frozen.a
sealed.a
(send Object isFrozen frozen)
(send Object isSealed sealed)
(send Object hasOwn sealed "a")))))))
;; Use case 10: switch example. The Racket surface form is case.
(define usecase-switch
(js
(define (switchExample n)
(case n
[(1) (return "one")]
[(2 3) (return "two-or-three")]
[else (return "other")]))))
;; Use case 11: class constructor with a default value.
(define usecase-class-constructor
(js
(define-class Greeter
(constructor ([name "world"])
(set! this.name name))
(method greet ()
(return (string-append "Hello " this.name))))
(define (classExample)
(let* ([a (new Greeter)]
[b (new Greeter "Ada")])
(return (array (send a greet) (send b greet)))))))
;; Use case 12: sort an array of objects by a given property.
(define usecase-sort-objects-by-property
(js
(define (sortByProperty xs prop)
(return (send (send xs slice)
sort
(lambda (a b)
(return (- (js-ref a prop) (js-ref b prop)))))))))
;; Use case 13: four ways to delete/remove an element from an array.
(define usecase-delete-array-elements
(js
(define (deleteArrayWays xs)
(let* ([a1 (send xs slice)]
[a2 (send xs slice)]
[a3 (send xs slice)]
[a4 (send xs slice)])
;; 1. Mutating removal with splice.
(send a1 splice 1 1)
;; 2. Functional removal with filter.
(set! a2 (send a2 filter (lambda (x i) (return (not (= i 1))))))
;; 3. Rebuild with slice + concat.
(set! a3 (send (send a3 slice 0 1) concat (send a3 slice 2)))
;; 4. delete leaves a hole and preserves length.
(delete-prop! a4 1)
(return (array a1 a2 a3 (array (send Object hasOwn a4 "1") (length a4))))))))
;; Use case 14: Bubble Sort.
(define usecase-bubble-sort
(js
(define (bubbleSort xs)
(let* ([a (send xs slice)]
[n (length a)])
(while (> n 1)
(let* ([i 1])
(while (< i n)
(when (> (list-ref a (- i 1)) (list-ref a i))
(let* ([tmp (list-ref a (- i 1))])
(vector-set! a (- i 1) (list-ref a i))
(vector-set! a i tmp)))
(set! i (+ i 1))))
(set! n (- n 1)))
(return a)))))
;; Use case 15: Binary Search using recursion.
(define usecase-binary-search
(js
(define (binarySearch xs target low high)
(if (> low high)
(return -1)
(let* ([mid (send Math floor (/ (+ low high) 2))]
[value (list-ref xs mid)])
(cond
[(= value target) (return mid)]
[(< value target) (return (binarySearch xs target (+ mid 1) high))]
[else (return (binarySearch xs target low (- mid 1)))]))))))
;; Use case 16: use Map to count how often each element occurs in an array.
(define usecase-map-count-occurrences
(js
(define (countOccurrences xs)
(let* ([counts (new Map)])
(for ([x (in-list xs)])
(if (send counts has x)
(send counts set x (+ (send counts get x) 1))
(send counts set x 1)))
(return (send Array from (send counts entries)))))))
;; Use case 17: get HTML in three different ways via the DOM.
(define usecase-get-html-three-ways
(js
(define (getHtmlThreeWays)
(return (array document.body.innerHTML
(js-dot (send document querySelector "body") innerHTML)
(js-ref (send document getElementById "root") "innerHTML"))))))
;; Use case 18: determine if stringA can be arranged into stringB.
(define usecase-anagram
(js
(define (sortChars s)
(return (send (send (send s split "") sort) join "")))
(define (canArrange stringA stringB)
(return (string=? (sortChars stringA) (sortChars stringB))))))
;; Use case 19: determine what pairs in an array equal a given value, with no
;; repeated numbers in the result pairs.
(define usecase-pairs-equal-target
(js
(define (pairsEqualTarget xs target)
(let* ([seen (new Set)]
[used (new Set)]
[out (array)])
(for ([x (in-list xs)])
(let* ([y (- target x)])
(if (and (send seen has y)
(not (send used has x))
(not (send used has y)))
(begin
(send out push (array y x))
(send used add x)
(send used add y))
(send seen add x))))
(return out)))))
;; Use case 20: fetch API, handling results and errors. The function returns a
;; Promise, which the test framework awaits.
(define usecase-fetch-api
(js
(define (loadTitle url)
(return
(send
(send
(send (fetch url)
then
(lambda (response)
(return (send response json))))
then
(lambda (data)
(return (object 'ok #t 'title data.title))))
catch
(lambda (err)
(return (object 'ok #f 'message err.message))))))))
(define all-js-usecases
`((random-number . ,usecase-random-number)
(unique-values . ,usecase-unique-values)
(falsey-values . ,usecase-falsey-values)
(currying . ,usecase-currying)
(object-destructuring . ,usecase-object-destructuring)
(timer-interval . ,usecase-timer-interval)
(object-props . ,usecase-object-props)
(string-concat-order . ,usecase-string-concat-order)
(freeze-vs-seal . ,usecase-freeze-vs-seal)
(switch . ,usecase-switch)
(class-constructor . ,usecase-class-constructor)
(sort-objects-by-property . ,usecase-sort-objects-by-property)
(delete-array-elements . ,usecase-delete-array-elements)
(bubble-sort . ,usecase-bubble-sort)
(binary-search . ,usecase-binary-search)
(map-count-occurrences . ,usecase-map-count-occurrences)
(get-html-three-ways . ,usecase-get-html-three-ways)
(anagram . ,usecase-anagram)
(pairs-equal-target . ,usecase-pairs-equal-target)
(fetch-api . ,usecase-fetch-api)))
(define (show-js-usecases)
(for ([entry (in-list all-js-usecases)])
(displayln (format "// ~a" (car entry)))
(displayln (cdr entry))
(newline)))
(define (write-js-usecases-file path)
(call-with-output-file path
#:exists 'replace
(lambda (out)
(displayln "// Generated by demo/js-usecases.rkt" out)
(displayln "// Each use case is wrapped in a function so snippets with return are valid." out)
(for ([entry (in-list all-js-usecases)])
(fprintf out "\n// ~a\n" (car entry))
(fprintf out "function run_~a() {\n~a\n}\n"
(regexp-replace* #rx"[^A-Za-z0-9_$]" (symbol->string (car entry)) "_")
(cdr entry))))))
(module+ main
(show-js-usecases))