Skip to content

Commit

Permalink
Merge pull request #5 from cratelyn/adverbs
Browse files Browse the repository at this point in the history
this branch implements the `/` and `\` adverbs.

for the corresponding reference pages in the J wiki, see:
* https://code.jsoftware.com/wiki/Vocabulary/slash
* https://code.jsoftware.com/wiki/Vocabulary/bslash

---

## ✨ examples

**monadic `/` "insert" adverb**

```
; cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/j`
  + / 1 2 3
6

  + / 1 2 3 4
10

  * / 1 2 3
6

  * / 1 2 3 4
24
```

**dyadic `/` "table" adverb**

```
; cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/j`
  1 2 3 4 5 * / 1 2 3 4 5
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

  1 2 3 4 5 + / 1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
6 7 8 9 10
```

**monadic `\` "prefix" adverb**

```
; cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/j`
  ] \ 1 2 3
1 0 0
1 2 0
1 2 3

  ] \ 1 2 3 4 5
1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
```

**dyadic `\` "infix" adverb**

```
; cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/j`
  3 ] \ 1 2 3 4
1 2 3
2 3 4

  3 ] \ 1 2 3 4 5
1 2 3
2 3 4
3 4 5
```
  • Loading branch information
cratelyn authored Nov 15, 2023
2 parents 104af14 + f489c32 commit c98657b
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 38 deletions.
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ the jsoftware wiki includes the following story concerning the original Incunabu
accordingly, j does not intend to be a fully-fledged implementation of an array language.

j values may be integers, arrays, or 2-dimensional matrices; higher-rank matrices are not
implemented. J's adverbs are not implemented. a small number of monadic and dyadic verbs are
provided. variables may be defined.
implemented. a small number of verbs and adverbs are provided. variables may be defined.

**monadic verbs**
* `i.` "idot" generates a value
Expand All @@ -34,6 +33,14 @@ provided. variables may be defined.
* `[` "left" returns the left value
* `]` "right" returns the right value

**monadic adverbs**
* `/` "insert" places a dyadic verb between items of its argument
* `\` "prefix" returns successive prefixes of its argument

**dyadic adverbs**
* `/` "table" returns a table of entries using a dyadic verb and two arguments
* `\` "infix" applies a verb to successive parts of its right-hand argument

variables are assigned using `=:`. variable names may only contain lowercase ASCII `a-z`
characters, numeric `0-9` characters, and `_` underscores. variable names must begin with a
lowercase ASCII `a-z` character.
Expand Down Expand Up @@ -332,12 +339,17 @@ taken together, the expression `1+i.4` adds `1` to each element of the array `0
`1 2 3 4`.

`/` is a kind of "adverb." in traditional human languages, an adverb is a part of speech used to
describe how a verb is performed. this same concept holds roughly true for J and K's adverbs.
apply an adjective to a verb. or in other words, it describes how a verb is/was performed. this
same concept holds roughly true for J and K's adverbs.

we won't comprehensively explain adverbs here. in short, `+ /` applies the dyadic `+` operator
"over" its argument(s).
adverbs and [gerunds][j-gerunds] are very similar to higher-order functions. a higher-order
function is a function that either accepts as an argument, or returns, another function. these
constructs provide a way for programmers to abbreviate or abstract over common control flow
patterns. J refers to the `/` adverb in this statement as "insert". this operator places the
dyadic `+` operator between the elements of its argument. thus, `+ / 1 2 3` is equivalent to
`1+2+3`.

so, the expression above has the following structure, expressed as an ascii diagram.
so, the expression above has the following structure:

```
+ / 1 + ! 100
Expand All @@ -350,7 +362,7 @@ so, the expression above has the following structure, expressed as an ascii diag
┝┳┥
┗━━━━━━━━━━━━━━━ find the sum of the given argument
┝━━━┳━━━━━━━┥
┗━━━━━━━━━━━━ sum "over" the sequence of numbers from 1 through 100
┗━━━━━━━━━━━━ add each of the numbers from 1 through 100
```

these programs share the same structure, save that `i.` is the verb for generating sequences,
Expand Down Expand Up @@ -693,4 +705,4 @@ todo...
[incunabulum]: https://code.jsoftware.com/wiki/Essays/Incunabulum
[why-k]: https://xpqz.github.io/kbook/Introduction.html#why-k
[api-guidelines]: https://rust-lang.github.io/api-guidelines/future-proofing.html
[j-gerunds]: https://code.jsoftware.com/wiki/Vocabulary/GerundsAndAtomicRepresentation
56 changes: 51 additions & 5 deletions src/a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,16 @@ use super::*; use std::marker::PhantomData as PD;
let(f)=|i_o,j_o|{i.get(j_o,i_o)};A::new(m_o,n_o)?.init_with(f)}
}

/**dyadic verbs*/impl A{
pub fn d_left (self,r:A)->R<A>{Ok(self)}
pub fn d_right(self,r:A)->R<A>{Ok(r) }
pub fn d_plus(self,r:A)->R<A>{A::d_do(self,r,|x,y|x+y)}
pub fn d_mul (self,r:A)->R<A>{A::d_do(self,r,|x,y|x*y)}
/**dyadic verbs*/impl D{
/*return dyad function**/ pub fn f(&self)->fn(I,I)->I{use D::*;
match(self){Plus=>D::add, Mul=>D::mul, Left=>D::left, Right=>D::right} }
/*add two numbers*/fn add (x:I,y:I)->I{x+y} /*multiply two numbers*/fn mul (x:I,y:I)->I{x*y}
/*left */fn left(x:I,y:I)->I{x } /*right */fn right(x:I,y:I)->I{ y}
} impl A{
pub fn d_left (self,r:A)->R<A>{Ok(self) }
pub fn d_right(self,r:A)->R<A>{Ok(r) }
pub fn d_plus(self,r:A) ->R<A>{A::d_do(self,r,D::add)}
pub fn d_mul (self,r:A) ->R<A>{A::d_do(self,r,D::mul)}
pub fn d_do(l@A{m:ml,n:nl,..}:A,r@A{m:mr,n:nr,..}:A,f:impl Fn(I,I)->I)->R<A<MI>>{
let(li,ri)=(l.as_i().ok(),r.as_i().ok());let(ls,rs)=(l.as_slice().ok(),r.as_slice().ok());
if let(Some(li),Some(ri))=(li,ri){r!(A::from_i(f(li,ri)))} // two scalars
Expand All @@ -160,6 +165,47 @@ use super::*; use std::marker::PhantomData as PD;
}
}

/**monadic adverbs*/mod adverbs_m{use super::*;
impl Ym{
/// using this adverb, apply the given dyadic verb to the provided operand.
pub fn apply(&self,d:D,a:A)->R<A>{use Ym::*;match(self){Insert=>Ym::insert(d,a),Prefix=>Ym::prefix(d,a)}}
fn insert(d:D,a:A)->R<A>{let mut v=a.vals();let(i)=v.next().ok_or(err!("empty"))?;v.fold(i,d.f()).try_into()}
fn prefix(d:D,a:A)->R<A>{
let d_=|i|{use{D::*};match(d){Mul=>1,_=>i}};
if let Ok(i)=a.as_i() {A::from_i(d_(i))}
else if let Ok(s)=a.as_slice(){let (m,n)=(s.len(),s.len());
let p=|i,j|if(j>i){Ok(0)}else{a.get(1,j).map(d_)};
A::new(m,n)?.init_with(p)}
else { bail!("monadic `\\` is not implemented for matrices") }}
}
// === monadic `/`, `Ym::Insert` tests
macro_rules! test_insert{($f:ident,$d:expr,$a:expr,$o:expr)=>
{#[test]fn $f()->R<()>{let(a):R<A>={$a};let(d):D={$d}; // typecheck macro arguments.
let i:I=a.and_then(|a:A|Ym::insert($d,a)).and_then(|a|a.as_i())?;
eq!(i,$o);ok!()}}}
test_insert!(add_a_scalar, D::Plus, A::from_i(42), 42 );
test_insert!(add_a_sequence, D::Plus, <A as TF<&[I]>>::try_from(&[1,2,3,4,5]), (1+2+3+4+5) );
test_insert!(mul_a_sequence, D::Mul , <A as TF<&[I]>>::try_from(&[1,2,3,4,5]), (1*2*3*4*5) );
}

/**dyadic adverbs*/mod adverbs_d{use super::*;
impl Yd{
/// using this adverb, apply the given dyadic verb to the provided operand.
pub fn apply(&self,d:D,l:A,r:A)->R<A>{use Yd::*;match(self){Table=>Yd::table(d,l,r),Infix=>Yd::infix(d,l,r)}}
fn table(d:D,l:A,r:A)->R<A>{let (l_i,r_i)=(l.as_i() ,r.as_i());
let (l_s,r_s)=(l.as_slice(),r.as_slice());
if let (Ok(l),Ok(r))=(l_i,r_i){let(i)=d.f()(l,r);A::from_i(i)}
else if let (Ok(l),Ok(r))=(l_s,r_s){let(m,n)=(l.len(),r.len());let(d)=d.f();
let f=|i,j|->R<I>{let(x,y)=(l[i-1],r[j-1]);Ok(d(x,y))};
A::new(m,n)?.init_with(f)}
else {bail!("unexpected fallthrough in Yd::table")}}
fn infix(d:D,l:A,r:A)->R<A>{let(s)=r.as_slice().map_err(|_|err!("infix rhs must be a slice"))?;
let(il)=l.as_i() .map_err(|_|err!("infix lhs must be a scalar"))?.try_into()?;
let(ic)=(s.len()-il)+1;
A::new(ic,il)?.init_with(|i,j|Ok(s[(i-1)+(j-1)]))}
}
}

/**deep-copy*/impl A<MI>{
pub fn deep_copy(&self)->R<A>{let A{m,n,l:li,d:di,i:_}=*self;A::new(m,n)?.init_with(|i,j|{self.get(i,j)})}
}
Expand Down
4 changes: 3 additions & 1 deletion src/j.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ fn eval_(ast:B<N>,st:&mut ST)->R<O<A>>{use{M::*,D::*};
Tally=>a.m_tally(),Transpose=>a.m_trans()}}
N::D{d,l,r}=>{let(l,r)=(rec(l)?,rec(r)?);match d{Plus=>l.d_plus(r), Mul=>l.d_mul(r),
Left=>l.d_left(r), Right=>l.d_right(r)}}
N::Ym{ym,d,o}=>{rec(o).and_then(|a|ym.apply(d,a))}
N::Yd{yd,d,l,r}=>{let(l,r)=(rec(l)?,rec(r)?);yd.apply(d,l,r)}
N::S{sy} =>{st.get(&sy).ok_or(err!("undefined symbol: {sy:?}"))?;todo!("symbol value clone")}
N::V{sy,e} =>{let(a)=rec(e)?;st.insert(sy,a);r!(Ok(None))}
N::E{sy,e} =>{let(a)=rec(e)?;st.insert(sy,a);r!(Ok(None))}
}.map(O::Some)}
Loading

0 comments on commit c98657b

Please sign in to comment.