• Laziness

    From luser droog@21:1/5 to All on Sat Sep 19 13:14:42 2020
    I've been playing with a new draft of my parser combinators,
    and I want to deal with laziness properly while the codebase
    is small so I have an idea of how to keep it working as it
    grows. So I'm going to talk through what I've got and what
    I'm thinking and feel free to jump in with a different idea
    or way to go about this.

    This code use my syntax extensions: https://codereview.stackexchange.com/questions/193520/an-enhanced-syntax-for-defining-functions-in-postscript

    Code is a little wide, comments may wrap.
    The function 'string-input' creates a lazy list of [char [row col]]
    structures in a lisp-like list of 2-element arrays for cons cells.
    The /cdr/ part of the cons cell is a recursive invocation of string-input wrapped in an executable array.

    `take' and 'drop' are the "API" functions for interacting with the
    lazy list. Some helper functions like 'rest' come from the struct2 library.

    (struct2.ps)run {
    string-input{ r c s }{ % r c s -> ( [s0 [r c]] {[s1 [r1 c1]] ... })
    s length 0 eq { null }{
    [ s head [ r c ] ]
    [ s head (\n) eq {r 1 add 0}{r c 1 add} ifelse
    s rest /string-input cvx ] cvx
    cons
    } ifelse
    } @func

    take{ x n }{ n 0 eq { zero }{
    /x load force dup zero ne { x-xs n 1 sub take cons } if
    } ifelse } @func
    drop{ n }{ n 0 gt {
    next n 1 sub drop
    } if } @func

    force { dup xcheck { exec force } if }
    next { dup second xcheck { dup 1 {force} update } if second } % force and update cdr
    update { 3 copy pop get exch exec put } % a i p a[i]=p(a[i])

    x-xs { dup first exch second }
    head { 0 1 getinterval }
    cons { 2 aa }
    aa { array astore }
    second { 1 get }
    zero { null }
    } pairs-begin

    0 0 (abcd\ne) string-input
    dup ==
    9 take ==
    0 0 (abcd\ne) string-input
    dup 2 drop == ==


    Output:
    $ gsnd -q -dNOSAFER pc11a.ps
    [[(a) [0 0]] {0 1 (bcd\ne) string-input}]
    [[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] [[(d) [0 3]] [[(\n) [0 4]] [[(e) [1 0]] null]]]]]]
    [[(c) [0 2]] {0 3 (d\ne) string-input}]
    [[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] {0 3 (d\ne) string-input}]]]

    So this much seems to be working. What I'd like to do now is write functions that can operate on "possibly lazy" data. I'm imagining a higher order
    function that takes an operation and wraps it in a lazy-safe wrapper that either does the operation of the data is not executable otherwise it wraps
    the data and operation into a new executable array and yield that.

    The function I'd like to use this way is 'x-xs' which just dumps the
    car and cdr on the stack.

    x-xs { dup first exch second }

    This actually seems too big to deal with all at once, so lets break it down.

    x-xs { dup x_ exch xs_ } % needs cons cell to be real
    x_ { first } % needs car to be real
    xs_ { second } % needs cdr to be real

    If the cons cell is *lazy* however, x-xs should instead wrap itself around
    it in a new lazy value.

    lazy-x-xs { { force dup x_ exch xs_ } curry } % when called, force inner value

    And similarly (?) for x_ and xs_, they'll need to wrap themselves.

    lazy-x_ { { first force } curry }
    lazy-xs_ { { second force } curry }
    pr better
    lazy-xs_ { { next } curry }

    'next' from above replaces the executable in the cdr with the real value
    and then calls second. But I suppose I need a similar function to update
    the car. Since my lazy list of characters only has lazy values in the
    cdr, I kind of have a blind spot for dealing with laziness in the car.

    Next there needs to be some decision layer to choose from the two behaviors.

    ?-x-xs { dup xcheck { lazy-x-xs }{ x-xs } ifelse }
    ?-x_ { dup xcheck { lazy-x_ }{ x_ } ifelse }
    ?-xs_ { dup xcheck { lazy-xs_ }{ xs_ } ifelse }

    I know I'm missing some connections in there. But does this roughly
    seem sensible? I'm think of adding it as a new syntax extension, like
    @func. Maybe, @lazy. It could decorate a function with all the extra
    business so ?-x-xs could be defined as

    ?-x-xs { dup x_ exch xs_ } @lazy

    and it would be transformed into the above.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luser droog@21:1/5 to luser droog on Sat Sep 19 19:17:13 2020
    On Saturday, September 19, 2020 at 3:14:43 PM UTC-5, luser droog wrote:
    I've been playing with a new draft of my parser combinators,
    and I want to deal with laziness properly while the codebase
    is small so I have an idea of how to keep it working as it
    grows. So I'm going to talk through what I've got and what
    I'm thinking and feel free to jump in with a different idea
    or way to go about this.

    This code use my syntax extensions: https://codereview.stackexchange.com/questions/193520/an-enhanced-syntax-for-defining-functions-in-postscript

    Code is a little wide, comments may wrap.
    The function 'string-input' creates a lazy list of [char [row col]] structures in a lisp-like list of 2-element arrays for cons cells.
    The /cdr/ part of the cons cell is a recursive invocation of string-input wrapped in an executable array.

    `take' and 'drop' are the "API" functions for interacting with the
    lazy list. Some helper functions like 'rest' come from the struct2 library.

    (struct2.ps)run {
    string-input{ r c s }{ % r c s -> ( [s0 [r c]] {[s1 [r1 c1]] ... })
    s length 0 eq { null }{
    [ s head [ r c ] ]
    [ s head (\n) eq {r 1 add 0}{r c 1 add} ifelse
    s rest /string-input cvx ] cvx
    cons
    } ifelse
    } @func

    take{ x n }{ n 0 eq { zero }{
    /x load force dup zero ne { x-xs n 1 sub take cons } if
    } ifelse } @func
    drop{ n }{ n 0 gt {
    next n 1 sub drop
    } if } @func

    force { dup xcheck { exec force } if }
    next { dup second xcheck { dup 1 {force} update } if second } % force and update cdr
    update { 3 copy pop get exch exec put } % a i p a[i]=p(a[i])

    x-xs { dup first exch second }
    head { 0 1 getinterval }
    cons { 2 aa }
    aa { array astore }
    second { 1 get }
    zero { null }
    } pairs-begin

    0 0 (abcd\ne) string-input
    dup ==
    9 take ==
    0 0 (abcd\ne) string-input
    dup 2 drop == ==


    Output:
    $ gsnd -q -dNOSAFER pc11a.ps
    [[(a) [0 0]] {0 1 (bcd\ne) string-input}]
    [[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] [[(d) [0 3]] [[(\n) [0 4]] [[(e) [1 0]] null]]]]]]
    [[(c) [0 2]] {0 3 (d\ne) string-input}]
    [[(a) [0 0]] [[(b) [0 1]] [[(c) [0 2]] {0 3 (d\ne) string-input}]]]

    So this much seems to be working. What I'd like to do now is write functions that can operate on "possibly lazy" data. I'm imagining a higher order function that takes an operation and wraps it in a lazy-safe wrapper that either does the operation of the data is not executable otherwise it wraps the data and operation into a new executable array and yield that.

    The function I'd like to use this way is 'x-xs' which just dumps the
    car and cdr on the stack.

    x-xs { dup first exch second }

    This actually seems too big to deal with all at once, so lets break it down.

    x-xs { dup x_ exch xs_ } % needs cons cell to be real
    x_ { first } % needs car to be real
    xs_ { second } % needs cdr to be real

    If the cons cell is *lazy* however, x-xs should instead wrap itself around
    it in a new lazy value.

    lazy-x-xs { { force dup x_ exch xs_ } curry } % when called, force inner value

    And similarly (?) for x_ and xs_, they'll need to wrap themselves.

    lazy-x_ { { first force } curry }
    lazy-xs_ { { second force } curry }
    pr better
    lazy-xs_ { { next } curry }

    'next' from above replaces the executable in the cdr with the real value
    and then calls second. But I suppose I need a similar function to update
    the car. Since my lazy list of characters only has lazy values in the
    cdr, I kind of have a blind spot for dealing with laziness in the car.

    Next there needs to be some decision layer to choose from the two behaviors.

    ?-x-xs { dup xcheck { lazy-x-xs }{ x-xs } ifelse }
    ?-x_ { dup xcheck { lazy-x_ }{ x_ } ifelse }
    ?-xs_ { dup xcheck { lazy-xs_ }{ xs_ } ifelse }

    I know I'm missing some connections in there. But does this roughly
    seem sensible? I'm think of adding it as a new syntax extension, like
    @func. Maybe, @lazy. It could decorate a function with all the extra
    business so ?-x-xs could be defined as

    ?-x-xs { dup x_ exch xs_ } @lazy

    and it would be transformed into the above.

    It seems to be working here's the decorator:

    lazy{ p }{
    { dup zero ne 1 index xcheck and }
    { force } /p load compose { curry } curry
    /p load { ifelse } curry curry
    compose
    } @func

    If I give it:

    { dup first exch second }

    it produces:

    {dup zero ne 1 index xcheck and
    {{force dup first exch second} curry}
    {dup first exch second} ifelse}

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)