Skip to content

JIK STEM navigation

Build · trade · test · learn

Innovate the way…

A social marketplace for software projects.

Find software, services, subscriptions, and specialists; evaluate deliveries, claim source, and learn in the Python-powered Lisp lab.

A little Jik, one idea at a time

Read the code. Follow the idea.

The falling code is a small language tour. Choose an example, explore its result, then try it in Lisp Lab.

Reach Master in JIK to create and edit rain examples for other learners.

native-scope.jik
(define (+ a b) 999)
[(+ 20 22) (#+ 20 22)]
Expected result[999 42]
01 / 49

Reach the native function

An ordinary name can be shadowed. A leading # looks in the native scope first, so #+ still adds these numbers.

In this runtime, #name falls back to the global scope when no native exists; it skips lexical bindings. The # is a lookup qualifier, not part of the symbol's name.

Try in Lisp Lab
basic-values.jik
[#nil #f #t 42 3.5
 (+ 0.1 0.2)
 "hello" .name 'answer]
Expected result[() #f #t 42 3.5 0.3 "hello" .name answer]
02 / 49

Meet the basic values

Nil, booleans, integers, decimals, strings, keywords and quoted symbols are values you can place in a vector.

The bundled printer writes nil as () and adds d to decimal output. A quote keeps answer as a symbol instead of looking up a binding.

Try in Lisp Lab
collections.jik
['(1 2 3) '(1 : 2)
 [1 2 3] {.answer: 42}]
Expected result[(1 2 3) (1 : 2) [1 2 3] {.answer 42}]
03 / 49

Give your data a shape

A quoted list, an improper pair, a vector and a hashmap each preserve a different shape. The colon separates a pair's head from its tail.

A leading dot makes .answer a keyword. The hashmap printer omits the optional colon between a key and its value.

Try in Lisp Lab
lambda.jik
((lambda [x] (#* x x))
 (#+ 2 3))
Expected result25
04 / 49

Evaluate, then call

A lambda receives evaluated arguments. Here addition produces 5 before the function squares x.

The parameter vector [x] names one argument. #([x] (#* x x)) is the compact form of this lambda.

Try in Lisp Lab
closure.jik
(define make-adder
  (lambda [n] (lambda [x] (#+ n x))))
(define add-five (funcall make-adder 5))
(funcall add-five 37)
Expected result42
05 / 49

Remember a lexical binding

The inner lambda captures n. The returned function still remembers 5 after make-adder has returned.

These functions are stored as values. funcall invokes them explicitly in Jik's separate value and function namespaces.

Try in Lisp Lab
rest-arguments.jik
((lambda [first : rest] [first rest])
 10 20 30)
Expected result[10 [20 30]]
06 / 49

Collect the remaining arguments

A parameter after : receives all remaining arguments. first gets 10, while rest collects 20 and 30.

This website's runtime represents rest arguments as a vector. An empty rest sequence is [].

Try in Lisp Lab
vau-raw.jik
((vau E [form] form)
 (#+ 20 22))
Expected result(#+ 20 22)
07 / 49

Receive the expression itself

A vau receives raw operands instead of evaluated values. Returning form gives us the addition expression, without running it.

E is the caller's environment. #[E [form] form] is the compact form of this operative.

Try in Lisp Lab
vau-eval.jik
((lambda [x]
   ((vau E [form] (eval E form))
    (#+ x 2)))
 40)
Expected result42
08 / 49

Choose when to evaluate

The operative explicitly evaluates its operand in the caller's environment E, where x is bound to 40.

In this runtime, eval takes the environment first, then the expression. An operative controls which operands it evaluates.

Try in Lisp Lab
macro.jik
(define-macro unless
  (vau E [test : body]
    `(if ~test #nil (begin ~@body))))
(unless #f (#+ 20 22))
Expected result42
09 / 49

Build a new expression

unless builds an if expression from raw operands. Backquote supplies the template; ~ inserts the test and ~@ splices the body forms.

The returned expression is evaluated at the call site. This example introduces no temporary bindings; it does not claim automatic macro hygiene.

Try in Lisp Lab
range-map.jik
(map (lambda [n] (#* n n))
     (range 1 5))
Expected result[1 4 9 16]
10 / 49

Transform a range

range includes its start and excludes its end. map applies the lambda to each element, producing the first four positive squares.

(range 5) starts at zero; (range 0) produces [].

Try in Lisp Lab
mutable-bindings.jik
(define score 5)
(set! score (+ score 7))
score
Expected result12
11 / 49

Update an existing cell

define creates a binding; set! updates the existing cell.

declare-value reserves a value name; definitions and function names use separate cells.

Try in Lisp Lab
parallel-sequential.jik
(define x 10)
[(let [[x 2] [y x]] [x y])
 (let* [[x 2] [y x]] [x y])]
Expected result[[2 10] [2 2]]
12 / 49

Choose parallel or sequential bindings

let evaluates initializers in the outer scope. let* exposes each new binding to later initializers.

Try in Lisp Lab
recursive-bindings.jik
(letrec [[fact (lambda [n]
  (if (= n 0) 1
      (* n (funcall fact (- n 1)))) )]]
  (funcall fact 5))
Expected result120
13 / 49

Bind a recursive local function

letrec creates the cells before filling them, so a closure can refer to its own binding.

Try in Lisp Lab
named-let.jik
(let count [[n 4] [total 0]]
  (if (= n 0) total
      (count (- n 1) (+ total n))))
Expected result10
14 / 49

Name a local loop

Named let gives the local iteration a function name. A call in tail position reuses the VM invocation loop.

Try in Lisp Lab
conditional-classification.jik
(define (classify value limit)
  (cond [(< value limit) .below]
        [(= value limit) .equal]
        [else .above]))
(classify 8 10)
Expected result.below
15 / 49

Classify with a public boundary

Compare the same two arguments in every branch, and return the keyword selected by the public contract.

and and or short-circuit; nil and f are false, while other values are true.

Try in Lisp Lab
named-function-cells.jik
(define answer 9)
(define (answer x) (+ x 3))
[answer (answer 6) (funcall @answer 7)]
Expected result[9 9 10]
16 / 49

Keep function and value names separate

A symbol can have a value binding and a function binding. @answer retrieves the function as a value.

Try in Lisp Lab
tail-accumulator.jik
(define (sum-to n total)
  (if (= n 0) total
      (sum-to (- n 1) (+ total n))))
(sum-to 7 0)
Expected result28
17 / 49

Carry the result in an accumulator

The recursive call is the final operation. The accumulator carries the work already done.

Try in Lisp Lab
arithmetic-operators.jik
[(+ 0.1 0.2) (- 17 5)
 (* 3 4) (/ 24 2)
 (mod 17 5) (abs -12)]
Expected result[0.3 12 12 12 2 12]
18 / 49

Calculate with exact decimals

Arithmetic accepts several operands where appropriate. Decimal values print without a suffix.

Division by zero is an error; use a condition before dividing when zero is a valid input.

Try in Lisp Lab
comparison-chains.jik
[(< 1 3 5) (<= 2 2 3)
 (= 7 7) (not f)
 (and (> 8 2) (< 8 10))]
Expected result[#t #t #t #t #t]
19 / 49

Read a comparison as a chain

Each adjacent pair must satisfy a chained comparison. not turns false into true and true into false.

Try in Lisp Lab
numeric-values.jik
[(+ (ratio 1 3) (ratio 1 6))
 (type-of 1.25)
 (type-of (complex 2 3))
 (type-of (quaternion 1 2 3 4))
 (character 65) (byte 12)]
Expected result[(ratio 1 2) .decimal .complex .quaternion #\A (byte 12)]
20 / 49

Name the numeric categories

Rationals retain exact fractions. Decimal, complex and quaternion values have distinct runtime identities.

Characters and bytes use explicit constructors; they are not ordinary strings or integers.

Try in Lisp Lab
mathematical-functions.jik
[(sqrt 81) (expt 2 6)
 (floor 3.75) (ceiling 3.25)
 (min 8 3 5) (max 8 3 5)]
Expected result[9.0 64 3 4 3 8]
21 / 49

Combine bounded mathematical functions

The native math functions operate on values through the runtime's bounded interface.

Try in Lisp Lab
pair-head-tail.jik
(define items (list 2 4 6))
[(car items) (cdr items)
 (length items) (null? ())
 (cons 2 4)]
Expected result[2 (4 6) 3 #t (2 : 4)]
22 / 49

Separate the head from the tail

A proper list ends in nil. A cons pair may instead have any value as its tail.

Try in Lisp Lab
vector-rest-view.jik
[(ref [5 10 15] 1)
 ((lambda [first : rest]
    [first (length rest) (ref rest 0)])
  5 10 15)]
Expected result[10 [5 2 10]]
23 / 49

Index a vector and inspect the rest

ref uses a zero-based index. Rest arguments expose the remaining sequence through a bounded view.

Try in Lisp Lab
keyword-map.jik
(define record {.name: "Rain", .score: 12})
[(ref record .name)
 (ref record .score)
 (hashmap? record)]
Expected result["Rain" 12 #t]
24 / 49

Find a value by its keyword

Keyword keys make a small record readable. ref looks up a key rather than a numeric position.

Try in Lisp Lab
checked-mutation.jik
(define items [1 2 3])
(define record {.score 4})
(set-ref! items 1 9)
(set-ref! record .score 12)
[items (ref record .score)]
Expected result[[1 9 3] 12]
25 / 49

Change one checked location

set-ref! updates an existing vector position or map entry. The runtime checks the target and index.

Try in Lisp Lab
filter-compose.jik
(define odds
  (filter (lambda [n] (= (mod n 2) 1))
          (range 1 7)))
(reverse (append odds [7]))
Expected result[7 5 3 1]
26 / 49

Filter before reversing

Each sequence operation returns a value that can feed the next operation. range excludes its end.

Try in Lisp Lab
left-fold.jik
(foldl (lambda [total item]
         (+ (* total 10) item))
       0 [2 4 6])
Expected result246
27 / 49

Fold from the left

foldl calls the function with the accumulator first and the next item second.

The initial accumulator is also the result for an empty input.

Try in Lisp Lab
text-operations.jik
[(string-append "Code" " rain")
 (substring "abcdef" 2 5)
 (string-upcase "rain")
 (string-contains? "rainfall" "fall")
 (string-length "rain")]
Expected result["Code rain" "cde" "RAIN" #t 4]
28 / 49

Measure, slice and search text

substring includes its start and excludes its end. Text searches are case-sensitive.

Try in Lisp Lab
runtime-predicates.jik
[(type-of 12) (number? 1.5)
 (string? "rain") (symbol? 'rain)
 (vector? []) (hashmap? {})
 (boolean? t)
 (callable? (lambda [x] x))]
Expected result[.integer #t #t #t #t #t #t #t]
29 / 49

Ask the runtime about a value

Predicates test JIK categories directly. type-of returns a keyword naming the category.

Try in Lisp Lab
receive-multiple-values.jik
(define-values [low high] (values 3 9))
(set-values! [low high] (values 4 8))
(call-with-values
  (lambda [] (values low high))
  (lambda [a b] [a b (+ a b)]))
Expected result[4 8 12]
30 / 49

Receive more than one result

values returns separate results. define-values and set-values! bind them, while call-with-values passes them to a consumer.

Try in Lisp Lab
function-as-value.jik
(define combine @+)
[(funcall combine 4 8)
 (apply combine [3 4 5])
 (map (lambda [n] (* n n)) [2 3])]
Expected result[12 12 [4 9]]
31 / 49

Pass a named function as a value

funcall supplies separate arguments; apply supplies an argument sequence. @ selects the function namespace.

Try in Lisp Lab
temporary-scope.jik
(define color .green)
[(scope (define color .amber) color)
 color]
Expected result[.amber .green]
32 / 49

Leave an outer binding intact

scope creates a local environment. Leaving it restores access to the outer binding.

Try in Lisp Lab
wrap-operative.jik
(define-function hold (vau E [x] x))
(define-function ready (wrap @hold))
[(hold (+ 4 8)) (ready (+ 4 8))]
Expected result[(+ 4 8) 12]
33 / 49

Wrap raw operands into evaluated arguments

An operative receives syntax. wrap makes an applicative that evaluates arguments before invoking the operative.

unwrap retrieves the underlying operative from a wrapped applicative.

Try in Lisp Lab
quasiquote-splice.jik
(define n 3)
(define tail [4 5])
`[1 ~n ~@tail]
Expected result[1 3 4 5]
34 / 49

Insert one value or splice a sequence

Backquote keeps a template as data. ~ inserts one value and ~@ inserts the elements of a sequence.

The outer vector remains a vector. Quoting a symbol preserves its identity instead of looking up a binding.

Try in Lisp Lab
prototype-inheritance.jik
(define base (object .base [] {.score 12}))
(define child (object .child [base] {}))
(object-set! child .name "Rain")
[(object-ref child .score)
 (object-ref child .name)]
Expected result[12 "Rain"]
35 / 49

Inherit a field from a parent

An object's own fields take precedence over inherited fields. Parents supply the prototype ancestry.

Try in Lisp Lab
generic-dispatch.jik
(#define-generic (describe x))
(#define-method (describe (#integer x))
  (+ x 1))
[(describe 5) (describe 6)]
Expected result[6 7]
36 / 49

Dispatch a method by type

A generic chooses an applicable method from the runtime type or prototype ancestry.

Repeated types can use the fixed-size dispatch cache. replace-method changes a method and invalidates cached dispatch.

Try in Lisp Lab
module-exports.jik
(define m (#module .m [answer] [double]
  (define answer 12)
  (define (double x) (* 2 x))))
[(module-ref m .answer)
 (funcall (module-ref-function m .double) 6)]
Expected result[12 12]
37 / 49

Export a value and a function

Modules have a private environment and separate value/function export lists. Only exported slots are accessible.

Try in Lisp Lab
continuation-escape.jik
(call/cc (lambda [exit]
  (+ 100 (funcall exit 12) 1000)))
Expected result12
38 / 49

Escape through a captured continuation

Invoking exit resumes the captured continuation. The pending additions are abandoned.

Try in Lisp Lab
continuation-reuse.jik
(define saved f)
(define turns 0)
(define (capture x)
  (+ 10 (call/cc (lambda [k]
    (set! saved k) x))))
(define result (capture 1))
(set! turns (+ turns 1))
(if (< turns 3)
    (funcall saved (+ turns 1))
    result)
Expected result13
39 / 49

Reuse a saved continuation

The saved continuation can outlive the call that captured it and can be invoked again.

The same snapshot resumes twice, with 2 and then 3. The shared turns cell stops replay after the third result; copied frames do not undo mutations to shared cells.

Try in Lisp Lab
register-compilation.jik
; Inspect instruction and call metrics in Lisp Lab.
(define (increment n) (+ n 1))
(increment 11)
Expected result12
40 / 49

Follow a call through the register VM

The reader builds forms, the compiler emits typed register instructions, and the VM executes them.

The web runtime uses format 16, 37 opcodes and a 32-register window. Public metrics describe execution; private bytecode is not returned.

Try in Lisp Lab
bounded-capsule.jik
; Repeated source can reuse a validated capsule.
; The worker still applies fuel and time limits.
(foldl @+ 0 (range 1 5))
Expected result10
41 / 49

Make a small bounded program

A cached program is validated and still runs inside the isolated worker with instruction, time and output limits.

A capsule digest identifies compiled content. A cache hit is not permission to access host files or bypass execution limits.

Try in Lisp Lab
diagnostic-stages.jik
; An unclosed ( is a reader error.
; (/ 1 0) is a runtime error.
(define (safe-div a b)
  (if (= b 0) .zero-divisor (/ a b)))
[(safe-div 12 3) (safe-div 12 0)]
Expected result[4 .zero-divisor]
42 / 49

Check syntax, then execution

Matching delimiters help before execution. Reader, compiler and runtime diagnostics identify different stages of failure.

Try in Lisp Lab
deterministic-checks.jik
(define (total xs) (foldl @+ 0 xs))
[(= (total [2 4 6]) 12)
 (= (total []) 0)
 (= (total [-2 2]) 0)]
Expected result[#t #t #t]
43 / 49

Check normal and empty inputs

A useful test set checks ordinary inputs, boundaries and cancellation. A complete result must match the expected value.

Keep tests deterministic. Project delivery validates an archive and its manifest without executing the uploaded project in the web process.

Try in Lisp Lab
foundation-report.jik
(define (tri n acc)
  (if (= n 0) acc (tri (- n 1) (+ acc n))))
(define (report value limit)
  [(cond [(< value limit) .below]
         [(= value limit) .equal] [else .above])
   (tri (abs value) 0)])
(report -6 2)
Expected result[.below 21]
44 / 49

Combine classification and a tail sum

The report combines two independent answers: a relation to the limit, and the triangular sum of the absolute value.

This is also a worked hint for the Practitioner cumulative contract, using a public teaching input.

Try in Lisp Lab
collection-report.jik
(define (report xs)
  [(foldl @+ 0 xs) (reverse xs) (length xs)])
[(report [2 5 8]) (report [])]
Expected result[[15 [8 5 2] 3] [0 [] 0]]
45 / 49

Report a collection without changing it

Build a vector containing sum, reversed copy and length. Empty input uses the fold's initial value.

This composition also supplies the Specialist cumulative report without mutating its input.

Try in Lisp Lab
text-report.jik
(define (report text needle)
  [(string-downcase text)
   (string-length text)
   (string-contains? text needle)])
(report "Code Rain" "Rain")
Expected result["code rain" 9 #t]
46 / 49

Return the three text answers together

Lowercase the displayed text, measure the original, and perform the case-sensitive search on the original.

Try in Lisp Lab
closure-report.jik
(define (report start step)
  (let [[advance (lambda [x] (+ x step))]]
    [(funcall advance start)
     (funcall advance (funcall advance start))]))
(report 10 3)
Expected result[13 16]
47 / 49

Apply a captured step twice

advance captures step. The second result applies the same closure to its first result.

Try in Lisp Lab
expert-composition.jik
(define (report xs text)
  (call-with-values
    (lambda [] (values
      (foldl @+ 0 xs)
      (reverse (map (lambda [x] (* 2 x)) xs))
      (string-upcase text)))
    (lambda [sum doubled upper]
      [sum doubled upper (length xs)
       (string-length text)])))
(report [2 4] "rain")
Expected result[6 [8 4] "RAIN" 2 4]
48 / 49

Compose a five-field report

Use multiple values to carry intermediate results to the final report constructor.

This follows the Expert public contract using sequences, strings and closures taught before that tier.

Try in Lisp Lab
delivery-checksum.jik
(define (report xs)
  [(length xs) (foldl @+ 0 xs)
   (foldl (lambda [acc item]
            (+ (* acc 31) item))
          0 xs)])
[(report [2 4]) (report [])]
Expected result[[2 6 66] [0 0 0]]
49 / 49

Carry a deterministic checksum

Each checksum step multiplies the old accumulator by 31 before adding the next item.

This is the public Master report shape: count, sum and checksum, including the empty-input case.

Try in Lisp Lab

Marketplace

Available projects

View all
Requested solutionUnsolved

Continuation Course Workbench

An unsolved, community-preordered project request for visual continuation tracing and exercises.

JIK / Fusion Lisp

JIK language 315+

by Miroslav Bezděk0.01 EUR

Network

People to know

View all

Miroslav Bezděk

JIK language designer and knowledge provider

JIK language, compiler, and VM author.

◉ English · ◉ Čeština
✎ English · ✎ Čeština

Available2 projects

Repository

Knowledge patterns

View all
Growth Free

aegwenia/jik/native-global-lookup

Symbols, namespaces, and # lookup

Keep # outside symbol identity; resolve #name native first and then global, with @ selecting the function namespace.

Unavailable Unlock 0
Initiate0/525
Growth Free

aegwenia/jik/reader-literals

Reader and literal values

Read nil, booleans, integers, decimals, strings, characters, bytes, keywords, symbols, lists, vectors, and maps. Bare t/f are Boolean fallbacks only when ordinary bindings do not shadow them; #t/#f are explicit literals.

Unavailable Unlock 0
Initiate0/525
Frontier Free

aegwenia/jik/definitions-bindings

Definitions and mutable bindings

Create value cells, update an existing cell, and distinguish declaration from definition.

Unavailable Unlock 15
Initiate0/525
Frontier Free

aegwenia/jik/lexical-binding

let, let*, letrec, and named let

Choose parallel, sequential, recursive, or loop-shaped lexical binding without exposing host state.

Unavailable Unlock 30
Initiate0/525
Frontier Free

aegwenia/jik/conditions-logic

Conditions and short-circuit logic

Branch and compose predicates while preserving the value of the selected form.

Unavailable Unlock 45
Initiate0/525
Frontier Free

aegwenia/jik/functions-lambda

Functions, parameters, and rest views

Create closures with fixed/rest parameters; parameters are lexical cells and rest arguments are bounded views.

Unavailable Unlock 60
Initiate0/525