~subsetpark/jnj

ref: cab71b923fc7bf642fb9c663c6329a5e6b27708d jnj/project.janet -rw-r--r-- 4.5 KiB
cab71b92 — Zach Smith remove second header 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
(declare-project
  :name "jnj"
  :description "J iN Janet"
  :authors ["Z. D. Smith <zd@zdsmith.com"]
  :license "GPL3"
  :repo "https://git.sr.ht/~subsetpark/jnj"
  :doc
  ````
  # JNJ (J iN Janet)

  JNJ provides bindings to the [J language][j] to be used in Janet code.

  J is a powerful array programming language in the APL lineage. It has a few
  features that might make it useful to expose from within a more
  general-purpose language:

  - Extremely terse and expressive notation for a very wide range of
  mathematical procedures
  - Highly optimized numeric code
  - Native support for arbitrary-dimensioned arrays

  ## Installation

  JNJ requires the presence of `libj.{so,dylib,dll}`, provided by the [J
  Language][j]. You might need to install an `-devel` package for J depending
  on your package manager.

  It will attempt to load `libj.so` at runtime. If `libj.so` is not present in
  your search path for `dlopen()`, you can specify the path when you install
  `jnj`:

  [j]: https://www.jsoftware.com/#/

  ```
  LIBJ_DIR=/directory/containing/sofile jpm install https://git.sr.ht/~subsetpark/jnj
  ```

  ## Usage

  See `eval/eval*` and `j/j*` for details.

  `jnj` is designed to allow you to use J from within Janet. Thus, it has two
  main functions:

  - using Janet to drive evaluation of J commands
  - conversion back and forth between Janet and J datatypes.

  To do so, jnj introduces two main abstract types:
  - a J Engine instance
  - a J Array object

  All J computations take place within an instance of the J Engine. For
  convenience, a default instance is created at import time and available at
  `jnj/j-engine`. The `eval` and `j` commands both use this default instance,
  while `eval*` and `j*` allow you to specify your own.

  All of the above functions have the same basic signature (modulo the presence
  or absence of an engine argument): they take a *verb* and 0, 1, or 2
  arguments.

  The difference is that `j` and `j*` will evaluate their J command and return
  native Janet datatypes. `eval` and `eval*`, on the other hand, will return
  the other abstract type, a *J Array*.

  J Arrays can be inspected for rank, shape, etc., but they can also be passed
  as arguments to further `eval`/`j` calls. Thus, they're an efficient way to
  perform more complex procedures, involving multiple calls to the J
  interpreter, without having to convert data in and out of the J
  representation. They are also the most efficient and convenient way to create
  multi-dimensional arrays, as opposed to nesting multiple Janet array/tuples.

  ### `eval`/`j`

  The verb can be any J sentence, verb or verb sequence. It can be a symbol or
  a string.

  If there are no arguments, the sentence will be evaluated and returned.

  ```
  repl:3:> (jnj/j "3 4 $ i. 10")
  ((0 1 2 3) (4 5 6 7) (8 9 0 1))
  ```

  Arguments should be native Janet terms *or* a J Array. If they're present,
  the verb will be applied to them. If there's a single argument Y, it will be
  evaluated in the form `<VERB> <Y>`. If there are two arguments, X and Y, they
  will be evaluated in the form `<X> VERB <Y>`.

  ```
  repl:4:> (jnj/j "$" [3 4] (range 10))
  ((0 1 2 3) (4 5 6 7) (8 9 0 1))
  ```

  ```
  repl:5:> (def mat (jnj/eval "$" [3 4] (range 10)))
  <jnj/jarray float [2/12]>
  repl:6:> (jnj/j '$ mat)
  (3 4)
  ```

  In the first example, we evaluate a J sentence using native datatypes and
  return the result as native datatypes. In the second, we evaluate the same
  command and return the result as a J Array, which we can then use as an
  argument in subsequent commands.

  ## Advanced Usage

  ### `let-j`

  The `let-j`/`let-j*` convenience macros provide a simple wrapper around
  `eval` and `j` which is more expressive and efficient.

  The above example could also be written as:

  ```
  repl:1> (let-j [mat ("$" [3 4] (range 10))]
            ('$ mat))
  (3 4)
  ```

  ### `to-j-array`

  As noted above, JNJ provides the J Array abstract type as a way to
  efficiently store J values. J provides an efficient and terse way to
  construct multi-dimensional arrays; however, if you have some existing Janet
  data which you want to use as an argument to J, you can convert it:

  ```
  repl:2:> (to-j-array [[1 1 1][2 2 2][3 3 3]])
  <jnj/jvalue float [2/9]>
  repl:3:> (j "+/" _)
  (6 6 6)
  ```
  ````)

(def rpath-fallback "/usr/lib64/j/j64/")

(defn rpath
  []
  (os/getenv "LIBJ_DIR" rpath-fallback))

(declare-native
  :name "jnj-primitives"
  :cflags @[]
  :lflags @["-ldl"
            "-Wl,--rpath" (rpath)]
  :source @["jnj.c" "j.c"])

(declare-source
  :source @["jnj.janet"])