Arc Forumnew | comments | leaders | submitlogin
1 point by diiq 4118 days ago | link | parent

Sorry, I'm still not understanding your proposal. From what I can tell, your example assumes that we can fully type the outputs of functions as well as inputs; I don't see that as a particularly easy thing. Just like any duck-typed language, the return type can be dependent on runtime data. Take, for instance:

    (def pathologic ('a b)
        (if b 
           (fn (c 'd) (,a c ,d))
           (fn ('c d) (,a ,c d))
And then:

    ((pathologic one-thing another) (list 3 4) '(a b c))
I can't type the function in this expression until the time of evaluation; the same is generally true of higher order functions.

In response to declare leak: I'm not concerned with optimization right now, more with the parsimony and elegance of the language.

In response to your interpreter &c., yes, the idea of first-class macros isn't new. Picolisp has been doing it since at least 2002. As far as I know, Eight is the only language that does so while maintaining lexical scope.

1 point by rocketnia 4118 days ago | link

Right, things can get pretty hairy, and sometimes it won't be possible for the compiler to figure out whether a given expression will be needed unevaluated at runtime, in which case those expressions need to be preserved somewhere in the compiler's result, just in case. The preserved code could still be compiled, too, if the application's file size isn't as important as its speed, or if doing that would save from having to bundle an interpreter in there too. (Take a look at 'compose. If it's exposed in a compiled module, the code (g x) has to be preserved.)

But even for your 'pathologic example, a compiler might be able to determine that the result of the expression must be some result that one-thing could produce. That's because the 'a parameter, 'one-thing, is known at compile time to be a true value (since it's a symbol), and so both of the possible function types successfully accept two parameters and result in something 'a (one-thing) would result in when called.

This might be a bit tough for a compiler to do until it's really mature, but it does fit within the scope of a type system. In particular, the type of (if b x a y 5) might be expressible using an "if" type combinator, which I imagine can be manipulated and simplified as long as there's a way to ask a type whether its values are guaranteed to count as true or false.

More and more pathological examples could be built, though, and that's the halting problem for you.

I'm not concerned with optimization right now, more with the parsimony and elegance of the language.

As it should be. ^_^ Compiling is nothing if not one big optimization, though, so optimization was what I was talking about.

In a non-optimization defense of declare quick-syntax-transformer, it could also potentially make IDE integration slightly better, since the IDE would be able to determine more about the code being generated.

But yeah, it's not very elegant to have something in the language that programs appear to work just as well without, and it's not very parsimonious to have something in the language spec which is likely to mean nothing to lots of implementations. It's just something to keep in mind if there turns out to be a clever tweak to the language that'll help everybody.


1 point by diiq 4117 days ago | link

Compiling is nothing if not one big optimization, though, so optimization was what I was talking about.

Ah. There's our confusion. I define compiling as the transformation of code in one language to code in another (lower level) language.

Sure, Eight can be optimized. I don't see how it can (by my definition) be compiled.


1 point by rocketnia 4117 days ago | link

I was thinking about adding "(and translation)" to that sentence, but I thought that was the easy part. After all, you can just write an interpreter that compiles to the lower level language and package it up with every Eight program. It isn't an especially insightful translation, perhaps, but it's a start.


1 point by diiq 4116 days ago | link

Yes, I concede --- you're right. I should be thinking in terms of partial evaluators, rather than full compilation.

With your permission, I'd like to cp this conversation to the Eight github-wiki, so we can stop cluttering up the Arc Forum ;)


1 point by rocketnia 4116 days ago | link

Sure, that's fine by me.

Speaking of partial evaluators... I was thinking about this topic some more yesterday, in regards to my everything-is-a-multimethod language idea, and I came up with this. I imagine having

  (aif a b c d e)
translate something like this:

  (%apply %compile-to-apply '(aif a b c d e))
Here, %apply is a special value that the compiler knows will evaluate all its parameters, then apply the first parameter to the next. Meanwhile, %compile-to-apply actually breaks down the syntax passed to it so that there's a simpler %apply to execute. Since %apply, %compile-to-apply, and '(aif a b c d e) are constants, a partial evaluator can simplify this to:

  (%apply (%compiler-of aif) '(a b c d e))
Here, %compiler-of is yet another compiler-internal procedure that looks at the signature of its argument in order to produce another procedure that does the actual work of compiling a parameter list to a simpler %apply form. Since (%compiler-of aif) is usually a constant and doesn't usually have side effects, this can be evaluated some more at compile time:

  (%apply ??? a 'b '(c d e))  ; where ??? is an implementation of aif's body
Even if aif isn't a constant, as long as the compiler knows enough about what aif will be (its type), it might be able to reduce (%compiler-of aif) anyway.

Now is when (%apply ??? a 'b '(c d e)) would be inlined if possible, in the hopes of compiling '(c d e). With all of Eight's 'leak, 'comma, and 'asterix, I think that might take a few more techniques than I've thought about. My language's approach to afn would probably work a bit more like this:

  (%apply (%compiler-of aif) '(a b c d e))
  (%apply ??? (list (fn () a)
                    (fn (it) b)
                    (fn () c)
                    (fn (it) d)
                    (fn () e)))
...and therefore avoid having to working with syntax in the method body. Then again, this language is probably going to be considerably more mind-melting to try to read. Here's a generous mockup of the kind of thing I have in mind for the base language (which I'm calling Blade):

  (def (aif (-repeat branch (condition) (consequence it))
            (-optional (else) (fn ())))
    (with-first (finding-on branch
                  (= it (condition)))
      (consequence it)
      else: (else)))
I'm not sure whether you actually wanted to know any of this stuff, heh, but I hope it helps. ^_^


1 point by rocketnia 4118 days ago | link

Your example brings up an interesting side point: 'fn has a special interaction with , and *? Otherwise, ,d and ,c are referring to top-level things. Sounds like 'let probably has the same interaction, since if you could do (let it it ,then), you wouldn't need ,(leak 'it then). Is this something velcros can do too? Something like (def my-let ('var val ... ''body) ...)? ^_^ I'm probably going to check out the Eight interpreter myself at some point, but I figured I'd ask.


1 point by diiq 4117 days ago | link

, and * are just read-macros. ,a -> (comma a) a -> (asterix a)

The lambda-list of fn* is: ('lambda-list ... 'body) so ,c and ,d are not evaluated at the time of function creation.

Let is defined in terms of fn.

I'm not sure what behavior you're hoping for from that double quote.


1 point by rocketnia 4117 days ago | link

Ah, the fact that they're shorthand is what I expected, I was under the impression that even so, they were expanded before the velcro was finally called. Otherwise

  (aif *elses)
seems like it wouldn't work. Time for me to go get the interpreter.