Arc Forumnew | comments | leaders | submitlogin
3 points by akkartik 3974 days ago | link | parent

I haven't actually played with it, but from reading the docs[1] Julia's macros -- like Dylan's or Nemerle's -- feel clunky compared to orthodox lisp. The clunkiness isn't caused by infix, though. Infix is easy to desugar, which might be easier to see with wart:

  $ git clone http://github.com/akkartik/wart
  $ git checkout c73dcd8d6  # wart is currently (and perhaps indefinitely) broken
  $ ./wart
  'a+b
  => (+ a b)
No, the real trouble lies in their use of f(x) syntax for function calls rather than (f x). As a result there's no way for a when macro to look like the builtin if keyword.[2]

That said, I'm sure it's useful to have even some clunky way to do code-generation. The key is that they stuck with a purely expression-based language, and didn't give in to the siren song of statements.

[1] http://docs.julialang.org/en/latest/manual/metaprogramming

[2] http://arclanguage.org/item?id=16924



2 points by malisper 3974 days ago | link

I just quickly tried wart and it does seem pretty impressive to me. The only thing I'm worried about is the rules for how something is grouped. I may just need to use it more to see if it's an actual issue, but I feel that having all of the different rules can make it very easy to write hard to find bugs. Other than that I find wart to be a very interesting idea which will be worth checking out.

Also is there any way to see what my code looks like if I were to type it using parenthesis?

-----

2 points by akkartik 3974 days ago | link

Thanks for trying it out! I've tried very hard to keep the rules minimal and easy to understand. Only two sentences in https://github.com/akkartik/wart/blob/c73dcd8d6/004optional_... are rules:

1. "Multi-word lines without leading parens are implicitly grouped with later indented lines."

2. "Indentation-sensitivity is disabled inside parens."

That's really all there is to it.

> is there any way to see what my code looks like if I were to type it using parenthesis?

It's a little clunkier than visualizing infix, but you can introspect on any function or macro. Here's an example session:

  $ ./wart
  ready! type in an expression, then hit enter twice. ctrl-d exits.

  def (foo x)
    if a b
      c d
      :else e

  foo
  => (object function {sig, body, })  # wart doesn't print table values by default

  (body foo)
  => ((if a b (c d) (:else e)))
Here you can see that turning (c d) and (:else e) into calls is probably not desired.

-----

3 points by malisper 3974 days ago | link

I kept looking and found that read has the functionality I was looking for.

  (read)
  5+5

  => (+ 5 5)
I also noticed that if I want to generate lists which are expressions, it is not as straightforward as it should be (I'm trying to get a list of the number 5 the symbol + and 5 again but wart for whatever reason rearranges them).

  '(5 + 5) 
   => (+ 5 5)
I'm going to guess this is a bug.

-----

1 point by akkartik 3974 days ago | link

Nice!

Yeah, that's a bug. I haven't yet bothered to create a version of read without the infix transform. For now you have to:

  '(5 (+) 5)
If you decide to try to fix this I'd love to hear your experiences. The point of wart was to be easy to hack on, but I'm losing steam because it's been hard to get feedback on that score.

-----

2 points by malisper 3974 days ago | link

I'm going to have to familiarize myself with the internals before I actually try to fix it. After looking for a little bit, I think the issue is in the transform_infix function where it handles the quoting.

  if (is_quote_or_unquote(n.elems.front())) {
    list<ast_node>::iterator p = n.elems.begin();
    while (is_quote_or_unquote(*p)) {
      trace("infix") << "skipping past " << *p;
      ++p;
      if (p == n.elems.end()) {
        trace("infix") << "just quotes/unquotes " << n;
        return n;
      }
    }
I would think that after you determine that the first element is a quote, you would just ignore the rest instead of going through and checking which ones are also quoted. Since I'm not that familiar with c++ or how the internals of wart work, I'm going to have to leave it to you to see if this is actually the problem.

-----

1 point by akkartik 3974 days ago | link

Thanks for the investigation! Yes, that would be the way to disable transforms inside a quoted s-expr.

But I think that still would leave issues. For one, it is approximately as likely that a list innocuously contains a literal '+ as that we're constructing a fragment of code that is eventually intended to be eval'd. We need a way to say, "this is code" or "this is never going to be eval'd." A second issue is that quoting isn't the only way to read data. Imagine using read to read a list from a file. How would we suppress infix there?

I actually think reading data from a file is the bigger issue. Small quoted lists in code will be noticed, and can be replaced with some (klunky) combination of list and cons. It's far worse if you have a multi-megabyte file that silently gets corrupted because of one character.

-----

2 points by akkartik 3973 days ago | link

Another issue with just disabling transforms inside quotes. This would stop working:

  ',car.x

-----

2 points by malisper 3974 days ago | link

I'm just wondering if the second rule is really such a good idea. If one wants to use indentation-sensitivity within parens, it is impossible. I'm not sure how often trying to write code like that would come up in practice, but I think the programmer should have the option in that case of whether indentation-sensitivity is actually being used (by using brackets or something else to make it clear).

-----

2 points by akkartik 3974 days ago | link

I'm happy to rethink it if you can suggest an alternative way to suppress indent-sensitivity. I didn't have the second rule at the start, but was swayed by a discussion with the "readable s-expressions" project: https://www.mail-archive.com/readable-discuss@lists.sourcefo...; https://www.mail-archive.com/readable-discuss@lists.sourcefo.... It makes for an easily understood rule. What I had before took more than two sentences to explain: https://github.com/akkartik/wart/tree/7db61146b0#readme (search for 'suppress grouping')

-----

2 points by malisper 3973 days ago | link

Just have brackets (maybe curly braces) use indentation sensitivty.

  def (foo x y z)
    if [and
          x = 5
          y = 10
          z = 15]
      prn "success"
      prn "failed"
instead of

  def (foo x y z)
    if (and (x = 5) (y = 10) (z = 15))
      prn "success"
      prn "failed"
or

  def (foo x y z)
    if (and (x = 5)
            (y = 10)
            (z = 10))
      prn "success"
      prn "failed"

-----

1 point by akkartik 3972 days ago | link

But you have to have some way to suppress indent-sensitivity. How would you represent these?

  '(a reeeeeaaaally
    long line)

  def (foo a b c
           d e f)
    ..
Oh, are you suggesting suppressing indentation inside () but not inside []?

-----

2 points by malisper 3972 days ago | link

Yes, parens will not use indentation sensitivity, but brackets will. In that case the examples you gave would be valid code.

-----

1 point by akkartik 3972 days ago | link

Ok, interesting idea that I hadn't considered before.

As it happens, I've been watching a discussion about a different use for brackets: http://lambda-the-ultimate.org/node/4879. Lots of people (scheme, clojure, this liso guy) seem to have an idea about what to use brackets for, and it's not clear what the best use is for these precious punctuation characters.

-----

2 points by malisper 3972 days ago | link

I realized that we actually don't need to have a closing bracket to signal the end of the expression. We just need to implement something like the $ operator in haskell.

  def (foo a b c)
    if $ and
           a = 5
           b = 10
           c = 15
      prn "success"
      prn "failed"
There should be enough information based off of the indentation to tell how it should be parsed. $ should mean something along the lines of everything indented after the next symbol is a single expression.

-----

2 points by akkartik 3972 days ago | link

You'll like the old discussion on Bullet which had some similar ideas, including considering how to suppress paren-insertion inside such a special operator, etc.

http://web.archive.org/web/20120210015823/http://seertaak.po... (http://arclanguage.org/item?id=15769; http://www.reddit.com/r/programming/comments/pekx5/bullet_ad...)

-----

1 point by akkartik 3971 days ago | link

I finally installed Julia, and my comment needs a correction. Macros sometimes don't need the parens.

  julia> macro foo(ex)
	   :($ex ? nothing : error("Assertion failed: ", $(string(ex))))
	 end

  julia> @foo 2 > 0

  julia> @foo 2 > 3
  ERROR: Assertion failed: :((2>3))
   in error at error.jl:22
However, I have not been able to get this to work with multiple args:

  julia> macro foo(ex, msg)
	   :($ex ? nothing : error("$($msg): ", $(string(ex))))
	 end

  julia> @foo(2 > 3, "aaa")
  ERROR: aaa: :((2>3))
   in error at error.jl:22

  julia> @foo 2 > 3, "aaa"
  ERROR: wrong number of arguments
I don't know if this is a real limitation, or I just don't know enough yet.

-----