Useful Tacit function
| (require tacit) | package: Tacit |
source code: https://github.com/lurry-m/tacit
1 Some renamed identifier
Firstly, there are some shortcuts for renamed procedures.
value
id : procedure? = values
value
° : procedure? = compose
value
~ : procedure? = curry
value
~~ : procedure? = curryr
value
: : procedure? = const
2 The different variant of the fork-macros
These macros are inspired by APL (J).
syntax
(fork (first ...) second ...)
> (define average (fork (/) sum length)) > (average (range 10)) 9/2
> (define euler-form (fork (make-rectangular) cos sin)) > (euler-form pi) -1.0+1.2246467991473532e-16i
> (define positive-even-number? (fork (and) number? positive? even?))
> (map (fork (cons) id positive-even-number?) (range 5)) '((0 . #f) (1 . #f) (2 . #t) (3 . #f) (4 . #t))
> (define displayln-and-negate (fork (begin) displayln -)) > (map displayln-and-negate (range 3))
0
1
2
'(0 -1 -2)
syntax
(fork2 (first ...) second ...)
syntax
(fork3 (first ...) second ...)
syntax
(fork* (first ...) second ...)
> (define sqr-and-subtract (fork* (-) sqr sqr sqr)) > (procedure-arity sqr-and-subtract) 3
> (define pythagorean-triple? (° zero? sqr-and-subtract)) > (pythagorean-triple? 5 4 3) #t
> (pythagorean-triple? 6 4 3) #f
> (define square-and-sum (~ foldl (fork* (+) sqr id) 0)) > (sum (map sqr (range 10))) 285
> (square-and-sum (range 10)) 285
> (foldl (fork* (+) id /) 1 '(15 7 3)) 355/113
The fork* is useful in combination with foldl. The square-and-sum is more performant and the last line is an example of how to calculate the continued fraction of pi: [3;7,15,1].
3 Syntactic sugar for mutable objects
Vectors and hashes are useful but they can be a bit verbose. These two definitions provide a few helpful procedures out of the box wich are similar to struct.
syntax
(define-vector identifier my-vector)
> (define-vector v (make-vector 5 5)) > (v-set! 1 17) > (v-ref 1) 17
> (v-update! 2 sqr) > (v++ 3) > (v-- 4) > (v-length) 5
> v '#(5 17 25 6 4)
syntax
(define-mutable-hash identifier my-hash)
> (define-mutable-hash ht (make-hash (list (cons 1 1) (cons 2 2)))) > (ht-set! 3 30) > (procedure-arity ht-ref) '(1 2)
> (ht-ref 3) 30
> (ht-ref 4 16) 16
> (ht-ref! 4 12) 12
> (ht-ref 4 16) 12
> (ht-update! 3 sqr) > (ht++ 1) > (ht-- 2) > (ht-count) 4
> (ht-map cons) '((1 . 2) (2 . 1) (3 . 900) (4 . 12))
> (ht-for-each (° displayln list))
(1 2)
(2 1)
(3 900)
(4 12)
> (ht-has-key? 5) #f
> ht '#hash((1 . 2) (2 . 1) (3 . 900) (4 . 12))
4 Showcase: Y-Combinator
The fork-macro is actually similar to the S function in the SKI-calculus.
> (define K :) > (define I (fork () K K))
> (define P (fork (fork (fork ()) K) K))
> (define C (fork (fork (fork ())) (fork (K) K) (K I)))
> (define E (fork (fork (I)) (fork () I I))) > (I 5) 5
> (((P add1) sqr) 10) 121
> (((C add1) sqr) 10) 101
> (define Y ((C E) (P E)))
Then we can define a factorial function like that:
Alternatively, the fibonacci function:
5 Showcase: Easy Racket
This section is inspired by an article of The alleged unreadability of J. The last section showcased some of the power of the fork, but it is barely readable. Here is a showcase of how it can also be used to make the code more readable, especially in combination with contracts.
> (struct triangle (a b c)) > (define the id) > (define on id) > (define of id) > (define squares (~ map sqr)) > (define hypotenuse triangle-c) > (define other-two-sides (fork (list) triangle-a triangle-b)) > (define are-equal? =) > (define calculate °)
> (define/contract triangle-is-pythagorean? (-> triangle? boolean?) (fork (are-equal?) (calculate the sqr on the hypotenuse) (calculate the sum of the squares on the other-two-sides))) > (triangle-is-pythagorean? (triangle 3 4 5)) #t
> (triangle-is-pythagorean? (triangle 3 4 6)) #f