Modules and namespaces
A BPL file that •load runs is a module. A module has names of its own. •load returns a record of its public names, and adds no names to the code that loads it.
n←•load "lib/numeric.bpl"
n.factors 360 ⍝ 2 2 2 3 3 5BPL’s modules follow BQN’s namespaces. In BQN, a namespace is a kind of value of its own. In BPL, •load returns an ordinary record.
Taking names
A pattern takes names from the record by key, as in [factors: factorial:]. Each name reads the item with the same key. Names the pattern doesn’t mention stay in the record. Assignment covers patterns.
[factors: factorial:]←•load "lib/numeric.bpl"
factorial 5 ⍝ 120A record holds operators as well as functions and arrays. A dot path or a destructured name gives the operator back.
a←•load "lib/array.bpl"
10 -a.foldl 1 2 3 ⍝ 4
[foldl:]←a
10 -foldl 1 2 3 ⍝ 4Each •load runs the file again and returns a new record. Load a file again after you edit it to get the new version.
Records as namespaces
Any record also works as a namespace. Inside T.(expr), each key of T is a name for its item:
order←["price":2 3 5;"qty":10 20 1]
order.(+/price×qty) ⍝ 85Names that aren’t keys come from the code around the expression. A key hides an outer name it shares.
order←["price":2 3 5;"qty":10 20 1]
rate←0.5 ⋄ order.(price×≥rate) ⍝ 3 4.5 7.5A dfn’s argument, operand and recursion names, ⍺ ⍵ ⍶ ⍹ ∇ ⍢, are the exception. They always come from the call, even where a key has the same spelling:
f←{["⍵":0].(⍵+1)} ⋄ f 5 ⍝ 6Assignments inside the parentheses stay there, as assignments in a dfn do. To change a field, assign to its dot path, as in order.qty←5 5 5.
order←["price":2 3 5;"qty":10 20 1]
r←order.(qty←0) ⋄ order.qty ⍝ 10 20 1T⍎text does the same for code held as text, like Dyalog’s ns⍎text:
order←["price":2 3 5;"qty":10 20 1]
order⍎"+/price×qty" ⍝ 85A key that holds a function binds as a function. A module’s record therefore works as a namespace too:
n←•load "lib/numeric.bpl"
n.(factors 360) ⍝ 2 2 2 3 3 5An array with keys on its first axis also works as a namespace. Each key names its major cell, which in a matrix is a row:
prices←"cost" "sale":[2 3 5⋄4 4 9]
prices.(sale-cost) ⍝ 2 1 4A scope applies to one array. For an array of records, {⍵.(expr)}¨records evaluates expr in each.
Writing a module
A module is a file of BPL statements, run in order each time it’s loaded. Loading shows no results, but explicit ⎕← output appears. Every name the top level assigns is public, except a name that starts with _. A private name stays out of the record. The module’s own functions can still use it.
⍝ stats.bpl
_sq←{⍵×⍵}
mean←+/÷≢
var←{mean _sq ⍵-mean ⍵}
•load "stats.bpl" returns a record that holds mean and var. var still uses _sq when you call it.
Modules that use other modules
A path that starts with ./ or ../ is relative to the file that holds the code. A module finds the files beside it this way, whatever the working directory is. Other relative paths are relative to the working directory. •nget and •nput follow the same rule. A module reads a data file beside it with •nget "./table.csv".
A module that uses another module usually keeps it under a private name. The other module’s names then stay out of its record:
⍝ report.bpl
_stats←•load "./stats.bpl"
summary←{[_stats.mean ⍵;_stats.var ⍵]}
A public name exports on purpose. With stats←•load "./stats.bpl", the record of report.bpl would hold the stats record too. Destructured names are public as well: [mean:]←•load "./stats.bpl" makes mean one of the module’s names.
A file that loads itself, directly or through other files, is a DOMAIN error.
The library
The files in lib/ are modules. They load each other with ./ paths and private names: lib/graph.bpl holds lib/array.bpl as _array, and uses _array.dsp.
Names in the session
The names of a loaded module aren’t session names. ]names and name completion don’t list them. Take the names you use, or keep the record and use dot paths.
From Python, bpl('•load "lib/array.bpl"').py gives the record as a dict. Its functions are Function objects. Its operators are Operator objects, which you call with their operands to get a function:
from basedpl import bpl
a = bpl('•load "lib/array.bpl"').py
a['foldl'](bpl.fn('-'))(10, [1, 2, 3]).py # 4