(define (f x y z) ..)
and not: (define f (x y z) ..)
The reason: it directly maps the structure of a call to the code it expands to. This mapping is rendered starker when you throw infix into the mix: def (a = b)
..
mac f.x
`(,f ,x)
mac ++n
`(n <- (,n + 1))
# basic type-based dispatch
mac (lhs <- rhs) :case list?.lhs
..
All now legal wart: http://github.com/akkartik/wart/commit/ff999e5258. And I didn't need to make any changes to my reader, just redefine the macros def and mac.---
As I've been playing with infix in recent weeks, I've been idly wondering how far you could push these ideas. Lisper dogma seems to be that only lisp can do macros well because it has parens and homoiconicity. I'm growing to think this is a canard. But if more complex syntaxes can get elegant macros, how much syntax is too much? I speculate that I can build elegant macros into any syntax -- as long as function calls group functions with their args. If I'm right, this is a very valuable insight, and lisp's syntax for defun makes it hard to see. Especially since C programmers like myself can start taking out the space:
defun foo(x y z)
..thereby moving farther away from the light[1].The notation f(x) needs to die.
[1] I didn't even really focus on this until evanrmurphy brought it up: http://arclanguage.org/item?id=12659
I always thought that was just because Scheme tries to be minimal, and using the single special-form "define" for two different purposes helps with that. Scheme also does two different things with "let":
(let ((foo ...)) ...)
(let foo ((bar ...)) ...)-----
{foo x y z} -> ...
{bar x y z} -> ...
{~ x y z} -> ...
But I'll have to spend a while mulling on that to see if it pans out. In the meantime, my plan for functions is to use -> syntax to define an anonymous function, meaning that this: -> x y z (+ x y z)
Is equivalent to this Arc code: (fn (x y z) (+ x y z))
This looks great when the functions are last, which is frequent with JavaScript callbacks. And then "def" is kinda like Arc's "=": # Nulan
(def foo -> a b c ...)
(def foo 5)
; Arc
(= foo (fn (a b c) ...))
(= foo 5)-----
How about a language that's like Haskell/Shen: currying everywhere. This language would be based heavily on functions, of course, which are expressed with "->" as follows:
foo %x -> %x + 2
The above is equivalent to this in Arc: (def foo (x) (+ x 2))
Now, how this works is... everything to the left of the "->" is a pattern. Everything to the right is the function's body. If a variable starts with % it's local to the function, if not, it's global.As a quick test of this system, I went through some semi-contrived examples taken from http://en.wikipedia.org/wiki/Duck_typing
class Duck:
def quack(self):
print("Quack")
def fly(self):
print("Flap, Flap")
class Person:
def __init__(self, name):
self.name = name
def quack(self):
print("{} walks in the forest and imitates ducks to draw them".format(self.name))
def fly(self):
print("{} takes an airplane".format(self.name))
def quack_and_fly(duck):
duck.quack()
duck.fly()
quack_and_fly(Duck())
quack_and_fly(Person("Jules Verne"))
And here it is in this hypothetical currying-pattern-matching language: duck ->
[ quack -> prn "Quack"
fly -> prn "Flap, Flap" ]
person %n ->
[ quack -> prn "@%n walks in the forest and imitates ducks to draw them"
fly -> prn "@%n takes an airplane" ]
quack-and-fly [ quack %q fly %f ] -> %q; %f
quack-and-fly duck
quack-and-fly person "Jules Verne"
Wooow that is short! It also means that (except for % variables which are local) the pattern matching to the left of -> matches the function call.If you wanted to, you could use parens like Shen instead of no-parens like Haskell.
Some downsides? Well, since it's using currying, there could be some issues with that. In particular, variable argument functions wouldn't be possible. There's also potentially some scoping issues and such.
Overall though, I think this idea is cool enough to actually try and implement it in a simple interpreter, to figure out all the kinks.
-----
[ foo = 5 | bar = 10 ]
The above is a collection of patterns. Specifically, it has a "foo" pattern that maps to 5, and a "bar" pattern that maps to 10. Now, let's put this object into a variable: pattern = [ foo = 5 | bar = 10 ]
Now how do we extract the subpatterns? Like so: pattern [ foo ]
pattern [ bar ]
The above returns 5, and then 10. And we can extract multiple patterns at once: pattern [ foo | bar ]
The above returns 10. This largely removes the need for "as" patterns, which is something I found cumbersome to use. You can think of | as being kinda like "do". It will first call the foo pattern, then the bar pattern.Also, in Haskell you might write this:
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
In this language you might write this: [ fib 0 = 0
| fib 1 = 1
| fib %n = fib (%n - 1) + fib (%n - 2) ]
Hmm... I'm still working out the kinks in the object system...-----
duck =
quack = prn "Quack"
fly = prn "Flap, Flap"
person %n =
quack = prn "@%n walks in the forest and imitates ducks to draw them"
fly = prn "@%n takes an airplane"
quack-and-fly %x = %x quack; %x fly
quack-and-fly: duck
quack-and-fly: person "Jules Verne"
Wow! Even shorter! This reminds me of CoffeeScript's implicit objects: http://coffeescript.org/#objects_and_arrays -----
(duck) ->
[ quack -> (prn "Quack")
fly -> (prn "Flap, Flap") ]
(person %n) ->
[ quack -> (prn "@%n walks in the forest and imitates ducks to draw them")
fly -> (prn "@%n takes an airplane") ]
(quack-and-fly [ quack %q fly %f ]) -> (%q); (%f)
(quack-and-fly (duck))
(quack-and-fly (person "Jules Verne"))
I wasn't sure whether "quack" and "fly" should be in parens or not...-----
duck =
[ quack = (prn "Quack")
fly = (prn "Flap, Flap") ]
person %n =
[ quack = (prn "@%n walks in the forest and imitates ducks to draw them")
fly = (prn "@%n takes an airplane") ]
quack-and-fly [ quack %q fly %f ] = %q; %f
quack-and-fly duck
quack-and-fly person "Jules Verne"
Oh my, am I going to invent a dynamically typed Haskell with immutable objects? I guess next will be laziness and monads...-----
No, that's a reasonable way to solve it. But I still would like optional args, variable args, and vaus. Wheee a dynamic Haskell with vaus~
Hmm... with a little syntax, you could write this:
quack-and-fly: person "Jules Verne"
That's like the . operator in Haskell.-----
; Arc
(mac and args
(if args
(if (cdr args)
`(if ,(car args) (and ,@(cdr args)))
(car args))
't))
; Shen
(defmacro andmacro
[and] -> true
[and X] -> X
[and X | R] -> [if X [and | R]])
Because Shen macros operate on the entire code structure rather than the arguments of the macro, you can do silly stuff like replace the number 2 with the number 2.5: (defmacro twomacro
2 -> 2.5)
And now (+ 2 2) returns 5.---
-----
def (a = b)
..
Is this a function with parameter "a" assigned to value "b" by default? If so, what is the function's name? # basic type-based dispatch
mac (lhs <- rhs) :case list?.lhs
..
No clue what's going on here, unfortunately. Would you mind walking me through it?-----
When wart sees a symbol that is punctuation, it treats it as an infix operator, so that (a + b) and (a = b) work, etc.
As for :case, that's basically equivalent to "extend" in Arc. In other words, in Arc you write this:
(extend foo ... (bar ...))
But in wart you would write this: def foo ... :case (bar ...)
And this topic is about Common Lisp (defun foo ...) vs. Scheme (define (foo ...))---
So, if you put it all together, that means this:
def (a = b)
..
Is equivalent to this in Arc: (def = (a b)
..)
---And this:
mac (lhs <- rhs) :case list?.lhs
..
Is equivalent to this in Arc: (mac <- (lhs rhs)
(when (list? lhs)
..))-----
I would have to get used to not always seeing the function name first, but I like the symmetry this produces between definition and call.
Added: So when wart got infix (http://arclanguage.org/item?id=16775), Kartik gave this example for defining your own infix ops:
def (<>) (a b)
(~iso a b)
"<>" had to be in parens so that wart's infixey reader wouldn't try to swap it with "def". Now thanks to scheme-style grouping of the function name with its params, this definition can be written: def (a <> b)
(~iso a b)-----
Thanks Pauan for the clarifying comments!
-----