Useful Tacit function
1 The different variant of the fork-macros
fork
fork2
fork3
fork*
2 Syntactic sugar for mutable objects
define-vector
define-mutable-hash
3 Showcase:   Y-Combinator
4 Showcase:   Easy Racket
8.18

Useful Tacit function🔗ℹ

Laurent Müller <loeru@pm.me>

 (require tacit) package: Tacit

source code: https://github.com/lurry-m/tacit

1 The different variant of the fork-macros🔗ℹ

These macros are inspired by APL (J).

syntax

(fork (first ...) second ...)

Returns the a unary function that applies the all the second arguments to the input and then the first arguments on the result. fork can be used in various ways, because the content of the fork is not restricted to procedures.

Examples:
> (define average (fork (/) sum length))
> (average (range 10))

9/2

> (define euler-form (fork (make-rectangular) cos sin))
> (euler-form 3.141592653589793)

-1.0+1.2246467991473532e-16i

> (define positive-even-number? (fork (and) number? positive? even?))
> (map (fork (cons) identity 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 ...)

Similar to fork, returns the a binary function that applies the all the second arguments to the input and then the first arguments on the result.

Example:
> ((fork2 (list) + * vector) 5 7)

'(12 35 #(5 7))

syntax

(fork3 (first ...) second ...)

Similar to fork, returns the a ternary function that applies the all the second arguments to the input and then the first arguments on the result.

Example:
> ((fork3 (list) + * vector) 5 7 11)

'(23 385 #(5 7 11))

syntax

(fork* (first ...) second ...)

Similar to fork, but the procedure-arity is identical to the arguments provided in second.

Examples:
> (define sqr-and-subtract (fork* (-) sqr sqr sqr))
> (procedure-arity sqr-and-subtract)

3

> (define pythagorean-triple? (compose zero? sqr-and-subtract))
> (pythagorean-triple? 5 4 3)

#t

> (pythagorean-triple? 6 4 3)

#f

2 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)

This will create a few extra procedures, which might be useful. These are identifier-set!, identifier-ref, identifier-update!, identifier++, identifier-- and identifier-length.

Examples:
> (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)

This will create a few extra procedures, which might be useful. These are identifier-set!, identifier-ref, identifier-ref!, identifier-update!, identifier++, identifier--, identifier-count, identifier-map and identifier-for-each and identifier-has-key?.

Examples:
> (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 (compose 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))

3 Showcase: Y-Combinator🔗ℹ

The fork-macro is actually similar to the S function in the SKI-calculus.

Examples:
> (define K const)
> (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:

Examples:
> (define fac
    (fork (fork (if) zero? add1)
          (fork (fork (*) I)
                (P sub1))))
> ((Y fac) 5)

120

Alternatively, the fibonacci function:

Examples:
> (define fib
    (fork (fork (if)
                (fork (or) zero? ((C zero?) sub1))
                I)
          (fork (fork (+))
                (P sub1)
                (P ((C sub1) sub1)))))
> (map (Y fib) (range 10))

'(0 1 1 2 3 5 8 13 21 34)

4 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.

Examples:
> (struct triangle (a b c))
> (define the identity)
> (define on identity)
> (define of identity)
> (define squares (curry map sqr))
> (define hypotenuse triangle-c)
> (define other-two-sides (fork (list) triangle-a triangle-b))
> (define are-equal? =)
> (define calculate compose)
> (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