gdritter repos documents / master scraps / dynparams
master

Tree @master (Download .tar.gz)

dynparams @masterraw · history · blame

I'm still working on the Matzo language, and I need to finish the parser (as
I can now evaluate more programs than I can parse, and have code written to
an as-yet unimplemented module spec.) Here are a few features I'm
_considering_ but haven't implemented yet, and how they might interact:

## Dynamic Default Variables

This opens a small can of worms, so to be clear, dynamic variables will
reside in a different namespace than normal variables. I don't know yet
whether said namespace will be denoted with a sigil (say `#x`) or whether
you might have to pass a symbol to a function (like `get.X`) to access
their values. The idea is that certain functions will have sensible
defaults that one might want to override, and rather than explicitly
matching on arity, one can instead pass them in dynamically using a
particular syntax. In the examples below, I'll use an explicit syntax for
lookup of a dynamically scoped variable; in particular, `get.x.y` will
look up a dynamically scoped variable `x` (typically a symbol), and if it
hasn't been supplied, will use the default value `y`.

    foo :=
      let combine := get.Combine.cat in
        combine."a"."b"
    bar := foo with Combine := (\ x y . x ";" y)

    puts foo
    (* prints "ab", as it uses the default combiner `cat` *)
    puts bar
    (* prints "a;b" *)

This will continue down dynamically, so

    s1 := (get.Pn."e") " stares intently."
    s2 := "It is clear that " (get.Pn."e") 
            " is interested in what is happening."
    sent := se.<s1,s2>
    puts sent;
    puts sent with fixed Pn := "he" | "she" | "e"

will force `Pn` to be the chosen value in all subexpressions evaluated
underneath the `with` clause. (Notice that nondeterminism still
works in dynamically computed variables, so one must declare them as
fixed at the binding site if you want them to be computed exactly
once.)

## Text Combinators

I've used one of these above: right now, I have a few planned.

    puts wd.<"a","b","c">
    (* prints "abc" *)
    
    puts nm.<"a","b","c">
    (* prints "Abc" *)

    puts se.<"a","b","c">
    (* prints "A b c" *)

    puts pa.<"a","b","c">
    (* prints "A. B. C." *)

So effectively, they function as ways of combining strings in a more
intelligent way. I plan for them to do some analysis of the strings so that
they don't, say, produce extraneous spaces or punctuation. (The names are
of course short for `word`, `name`, `sentence`, and `paragraph`, respectively,
and may very well be alises for those longer names.)

## Rebindable Syntax

The conjunction of the previous two suggests that certain bits of syntax
should be rebindable. A prominent example is concatenation, e.g.

    puts "foo" "bar" "baz"
    (* prints "foobarbaz" *)

    puts "foo" "bar" "baz" with Cat := se
    (* prints "Foo bar baz.", as normal string concatenation
     * has been overloaded by `se` *)

    puts "foo" "bar" "baz" with Cat := fold.(\ x y -> x ";" y)
    (* prints "foo;bar;baz", as normal string concatenation
     * has been overloaded by a custom function *)

This could lead to some pretty phenomenal weirdness, though:

    f := \ x y -> add.x.y
    puts f.<1,2> where App := \ f x . fold.(\ g y . g.y).(append.f.x)
    (* prints 3, as we have overloaded function application to
     * automatically uncurry functions *)

...so maybe there should be a limit on it.

## Error Handling

Still not sure on this one. I don't particularly want to bring monads
into this, mostly because I want the language to be a DSL for strings
and not a general-purpose programming language, but at the same time,
it might be nice to have a simple exception-like mechanism. One idea
I was playing with was to implement a backtracking system so that
errors (both raised by users and by built-in problems) could simply
resume at particular points and retry until some retry limit is reached.
For example, you could reject certain unlikely combinations:

    x ::= a b c d
    wd := let result := x x x x
          in if eq.result."aaaa" then raise Retry else result
    puts mark Retry in wd

Here, `mark exp in wd` corresponds roughly to the following imperative
pseudocode:

    { retry_count := 0
    ; while (retry_count < retry_max)
        { try { return wd; }
          catch (exp) { retry_count ++; }
        }
    }

It's a much more limited form of exception handling, which may or may not
be desirable, but does give you some ability to recover from errors so long
as at least _some_ execution of your program will be error-less.

All this is heavily open to change, so we'll see.