Arc Forumnew | comments | leaders | submitlogin
Scala style match-case
9 points by raymyers 6117 days ago | 18 comments
Piggy-backing on the excellent work of almkglor, it is possible to make a generalized case statement that utilizes pattern matching.

    (require "lib/defpat.arc")
    (let trans
      (p-m:afn
        (()) '()
        ((x)) `(,x)
        ((x  y . ys))
          (let a (if (isa x 'sym) `',x x)
            `((,a) ,y ,@(self ys))))
      (mac pcase (var . cases)
        `((p-m:fn ,@(trans cases)) ,var)))
This behaves in the expected way for any typical use of case. For instance, case and pcase are interchangeable in this definition of translate:

    (def translate (sym)
      (pcase sym
             apple 'mela
             onion 'cipolla
             'che?))
On the other hand, consider the following way to merge two sorted lists:

    (def union (< xs ys)
      (pcase `(,xs ,ys)
        (xs ()) xs
        (() ys) ys
        ((x . xt) (y . yt))
          (if (< x y) (cons x (union < xt ys))
              (< y x) (cons y (union < xs yt))
              (cons x (union < xt yt)))))
This cannot be done straightforwardly with defpat / p-m:def because there is no equivalent to "xs@(x:xt)". With pcase, you can match over arbitrary bound variables. The construct (p-m:fn ... ) is roughly equivalent to (fn args (pcase args ... )), where the latter is preferred if you want to bind args.


3 points by almkglor 6117 days ago | link

Interesting work^^. The main problem currently plaguing me is the lack of nice syntax shortcuts. In order to add guards I co-opted the , and ,@ syntaxes already. Hmm. The problem is that there is currently no syntax that supports an <expr><punctuation><expr> or at least <symbol><punctuation><expr>. ! could have been co-opted but the problem is that ! works only for symbols.

-----

6 points by raymyers 6117 days ago | link

And if you like that, wait'll you see my new macro, hcase. ;)

    (def union (< xs ys)
      (hcase `(,xs ,ys)
        xs [] = xs
        [] ys = ys
        (x:xt) (y:yt) / (< x y) = (cons x (union < xt ys))
                      / (< y x) = (cons y (union < xs yt))
                      / otherwise = (cons x (union < xt yt))))

-----

3 points by almkglor 6115 days ago | link

code or it didn't happen ^^. That said, I fully intend to transform the p-m macro to use a different guard syntax, to wit:

  (p-m:def union
    (_ xs ())  xs
    (_ () ys)  ys
    (< (x . xs) (y . ys))
    || (< x y) (cons x (union < xs (cons y ys)))
    || t       (cons y (union < (cons x xs) ys)))

-----

3 points by raymyers 6114 days ago | link

It happened, and so did this. http://paste.lisp.org/display/56496 :)

    (hdef union
      _ xs [] = xs
      _ [] ys = ys
      < xs@(x:xt) ys@(y:yt) / (< x y) = (cons x (union < xt ys))
                            / (< y x) = (cons y (union < xs yt))
                            / otherwise = (cons x (union < xt yt)))

-----

3 points by almkglor 6114 days ago | link

Here's a suggestion: add the following bracket-detector stuff (untested):

  (let bracket-sample '[a.highly-unlikely!symbol]
    (def is-bracket (a)
       (is (car a) (car bracket-sample)))
    (def bracket-list (a)
       ((afn (a sample)
         (if
           (iso sample '(a.highly-unlikely!symbol))
              a
           (acons a)
              (or
                (self (car a) (car sample))
                (self (cdr a) (cdr sample)))))
        a bracket-sample)))
I then suggest you transform this code:

  (if (is 'make-br-fn (car a))
      (map transform-brackets (cadr a))
to:

  (if (is-bracket a)
      (map transform-brackets (bracket-list a))
This retains compatibility with Arcn, and allows bracket.scm to insert something other than 'make-br-fn.

-----

2 points by raymyers 6113 days ago | link

Yep, that would be an improvement. An even better way to do this might be to use a tree-based parser combinator library with embeddable semantics.

    (withs (bracket-contents nil
            bracket-pattern 
              `[,(sem [= bracket-contents _]
                      (many anything))])
      (carry-out (parse bracket-pattern '([something (in _) brackets])))
      bracket-contents)

  =>  (something (in _) brackets)
Yes, that one is real too.

-----

2 points by almkglor 6113 days ago | link

^^ again... code please! Seems pretty nice. Just wondering, does this use the parse combinator library on arc-wiki or is this something else? Haven't studied the parsec lib very well yet. If different, would it be possible to merge it into the arc-wiki lib/parsec.arc?

-----

3 points by raymyers 6113 days ago | link

Here ya go, lib/treeparse.arc. Unlike the sloppy proof of concept hcase, this one might actually be useful.

This is different from parsecomb.arc in a number of ways, one being that it operates on lists and trees while parsecomb takes strings. Parsecomb also appears to be broken...

-----

2 points by almkglor 6113 days ago | link

Thanks! I'll be reading it through a bit.

Incidentally, pg would like to conflate strings as a list of characters, so possibly with a "correct" implementation of treeparse we can still parse strings as well.

Possibly the brokenness in parsecomb currently is from the merge with arc1 and/or arc2

Edit: Hmm. Possibly you think this can be encapsulated using one of the module systems? Most of the components are functions anyway, and the existing macros end up evaluating to functions, so it may be possible to transform them to higher-level functions instead.

-----

2 points by raymyers 6113 days ago | link

It would be nice to put it in a module, seeing as it binds allot of useful words. I like your module1plus, but without macro support it is hard to contain this library.

The macros seq, alt, cant-see, many, and many1 may appear to be expressible as functions, but they need to be macros to reference parsers not yet created.

    (= a (alt 'y (many b)))
    ;; `b' is not bound yet, but will be by the time `a' is used.
    (= b (alt 'x (many a)))
    (parse a '(x y))

-----

1 point by almkglor 6113 days ago | link

Oh, bummer. I guess I got too used to lazy evaluation in Haskell.

Macro support... dang. It's kind hard to scan through the code looking for macro definitions, and then you need to have them exportable, meaning you need the original list - and exportable macros will not be able to refer to the exporting environment (bummer).

-----

1 point by raymyers 6109 days ago | link

Macros in packages are a hard problem, no doubt. I still wonder if some degree of macrolet hackery might do the trick.

On a brighter note, I decided to make the lazy semantics of "lib/treeparse.arc" a special case, not the default. That way, there only needs to be one macro, delay-parser. Apart from making the code cleaner, this also makes a module more feasible.

-----

1 point by almkglor 6109 days ago | link

The problem is an expectation like the following:

  (module
    (module-vars foo)
    (interface settings
      foo-option)
    (def foo-option arg
      (if arg
        (= foo (car arg))
        foo))
   (mac macro-using-foo body
     `(do ,foo ,@body)))
Hmm. Maybe export the macros as functions. Hmm. This also means that everything within the module has to be macro-expanded, in case macroexpansion creates a reference to a module-based macro. Aargh.

-----

1 point by raymyers 6113 days ago | link

On second thought, that example goes into an infinite loop. Here's a more relevant one that does not.

    (= br (filt list:cadr:car `[,(many pattern)]))
    (= pattern (alt br anything))
Mutually recursive patterns are still a bit sketchy.

-----

3 points by raymyers 6113 days ago | link

Neat! The efficiency police might not dig it, but I would really like to see strings as lists. Haskell does this, and I think it's a big win.

-----

1 point by absz 6113 days ago | link

Is there any reason not to do

  (withs (ahus (uniq) bracket-sample `[,ahus])
    ...)

?

-----

1 point by cooldude127 6113 days ago | link

uniqs aren't really important since these are just functions. if they were macros, that would be another story.

-----

1 point by almkglor 6113 days ago | link

Hmm. You appear to be correct, this might be better.

-----