Skip to content

moon-chilled/loop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is an implementation of Common Lisp's 'loop' macro for Scheme.
It originates in SICL (https://github.com/robert-strandh/SICL).

It's not portable, depending on a number of features specific to s7; however,
if you can do (single) dynamic dispatch it shouldn't be difficult to port.
Note also that I have largely tried to keep the code close to the original
where possible, rather than trying to make it idiomatic, so that it will be
easier to port future upstream improvements.

There is an existing implementation of the macro that has been ported fairly
widely; this, unlike that, is permissively licensed and supports a number of
additional features:

- Destructuring  (loop for (x y) in '((1 2) (3 4) (5 6)))
- Keywords  (loop :for i :below 5)
- Use of multiple values
  (loop for i below 5 collect (if (< i 3) (values) (values i i)))
  #| --> (3 3 4 4) |#
  (In many cases this can be obviated through clever use of conditional and
   conjunctional clauses.  For instance, the above example can be equivalently
   written as (loop for i below 5 if (>= i 3) collect i and collect i).)
- Use arbitrary iterators with 'across'
  (loop for (k . v) across (hash-table 'a 5 'b 6) collect k)
  (For this reason, hash-keys/hash-values are elided.
   For obvious reasons, iteration across packages is as well.)
- The 'select' clause is similar to 'collect' or 'append', but creates strings.
  For example: (loop for x in '(#\a "BC" #\d) select x into q select q)
  #| --> "aaBCaBCd" |#

Additional notes:

- Variables which are not explicitly initialized will be initialized to
  #<undefined> rather than nil
  (loop with x return x) #| --> #<undefined> |#
  - However, loops without an explicit result will return null.
    E.G. (loop repeat 1) #| --> () |#
- Type specifiers are parsed, but largely ignored.  The exception is
  uninitialized variables:
  (loop with x integer? return x) #| --> 0 |#
  I'm not sure what more there is to do with them; suggestions welcome!
  (The full list of types supported is: integer? let? list? hash-table? float?
   string? vector? byte-vector? float-vector? int-vector?.)
- When destructuring, the catch-all no-bind is ().  E.G.
  (loop with (x () y) = '(1 2 3 4) return whatever)
  ←→ (let ((x 1) (y 3)) whatever)
  In this case, () is the empty (de)structure which matches everything and
  binds nothing, placing no constraints; a case where (imho) null being a
  special object rather than a symbol leads to more elegant formulations.
- Minimize and maximize clauses that never run produce +inf.0 and -inf.0,
  respectively.  Maximize and minimize clauses cannot affect the same
  accumulation variable; hence:
  (loop repeat 0 maximize 5)   #| --> -inf.0 |#
  (loop minimize 0 maximize 0) #| error |#

Todo:

- Collect a vector instead of a list with 'volita'
- Collect major cells using 'volitas'?
- Collect into a lazy iterator?
- Figure out what to do with named loops (just use the name as a return function?)

About

Loop implementation for scheme

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published