Verifying my understanding of psychotic bastard:
set x: fn (b) : cons : 't (cons 'b ())
is a valid way to say
(set x (fn (b) (cons (' t) (cons (' b) ()))))
Correct?
Verifying my understanding of psychotic bastard:
set x: fn (b) : cons : 't (cons 'b ())
is a valid way to say
(set x (fn (b) (cons (' t) (cons (' b) ()))))
Correct?
Currently true code samples? | |
----- | |
set x: fn (b): cons 't (cons 'b ()) ===> (set x (fn (b) (cons (' t) (cons (' b) ())))) | |
set x: fn (b): cons 't: cons 'b () ===> (set x (fn (b) (cons (' t) (cons (' b) ())))) | |
----- | |
set def: | |
'('name ... 'code | |
leak: really name | |
set (really name) code) | |
and | |
set def: | |
'(('name ... 'code): | |
leak: really name | |
set (really name) code) | |
(set def | |
(' (((' name) ... (' code)) | |
(leak (really name)) | |
(set (really name) code)))) | |
[MAYBE this, too: | |
set def: | |
'('name ... 'code | |
leak: really name | |
set (really name) code)] | |
----- |
I'll write more tomorrow, but the most ridiculous solution to IV is that \ is an unary operator:
def \ ('args): fn (... 'code): fn (just args) (just code)
Which is oystery notation for a function that takes an arg list, and returns a function that takes a code block and returns a function.
You do need the parens there but I really think they're OK --- an arg list is a list, and can be denoted suchly.
But you're right, I'm being an armchair-designer again. I have to replace the glib parser anyway, might as well go after PB; it seems like the mostly-magic colon, line-continuing , and logical-line definitions are pretty much settled, so I can start from there.
(Hmmm. Does line-continuing-\ conflict with lambda-\ ?)
Ohmygoodness, I woke up with the most terrifying idea in my head (note: oif is a built-in if without an else)
((\(): leak if leak else set! do-else 't set! if: \(test ... 'code): set! do-else 't oif test: *code set! do-else () set! else: \(... 'code): oif do-else *code))
Make if
and else
actually be two separate functions that share a scope! This blows up when you use call/cc, but it makes me wonder if the right direction is manipulating the primitives to fit the syntax, rather than t'other way 'round.
More alarming solutions: a c-preprocessor style string-replace:
(set-parse "*" "<<binary-*>>")
(set-parse "else" " else")
OK, how far is the reach of an infix operator? Like, what does this translate to:
a: b: c (d) <<operator>> (e: f) g h: i
My first instinct is to call it at the colon and the end of line:
(a (b (operator (c (d)) ((e (f)) g h (i))))
Is that what you're calling the 'logical' line? What about this?
a: b <> c d: e f g i j k
I'm allowing
a b <<c d e>> f g
to mean
((c d e) (a b) (f g)
am I doing that right?
EDIT: This is called psychotic bastard not because it is a crazy idea but because attempting to parse it makes me want to kill.
Shit, and what about (damnit, pre can't handle <<
and >>
. Substituted [[
and ]]
)
a b [[operator]] d e: f g
is that
(operator (a b) (d e (f g)))
?
Also parens are hard, too
abbot and costello (are: going to the store to buy) some bread
(abbot and costello (are (going to the) (store to buy)) some bread)
?
(abbot and costello (are (going to the) (store to buy)) (some bread))
?
Or is it illegal? If so, what about:
(\x: do some things mostly to x I guess) 5
EDIT: I guess it HAS to be (abbot and costello (are (going to the) (store to buy)) some bread)
, or else everything falls apart.
Operators really need to be held in by colons on both sides, because the letting them run to the end of the line is wrong in block form. (I have this wrong in my parser right now, and didn't realize it was wrong until I tried to explain how it works. Essentially, an infix operator that comes before a colon and isn't in a paren shouldn't extend past the colon:
embrace [[compose]] extinguish: lotus novell # Requires def-syntax if a > b: print blammo
I'm not 100% sold on this; I've written a few snippets and know that there are some cases where you want to have colon syntax fire first:
header body: article1 article2
but I am about 98% sold.
Your Abbot and Costello edit is exactly right. The other function in that post is legal for me, but I thought not for you. Did you forget parens around the argument list, or have you come around on that one?
I had not considered <<
and >>
wrapping anything other than a single identifier. Interesting.
I don't know if we got this wrong anywhere else, but in your example
a: b: c (d) <> (e: f) g h: i
there is a colon that doesn't do anything (after e). The one after h only has the effect of limiting the scope of the operator. Both f
and i
don't get parens from the colons because they're already a single expression. IOW,
e: f
and
e: f
and
(e f)
are all the same.
Aside: Seriously, how did you people deal with closing parens until the right one flashes for decades? I wonder this every time I translate something back to s-expressions.
I knew I had a better example that's pro colon-fires-first:
rest-of-line <- many: indented-past block-indent >> basic-expr
Here <-
is roughly let
. The block this comes from is monadic, but that's not important right now, the gist is the let
/set
/define
operator should have the lowest precedence (colon should fire first).
(Also not important is that the line is ugly.)
Hmm.
Easy answers first:
I came around on the arg-list-parens for a single argument; I still think you're crazy for wanting \x y z: x + y + z
.
Re (e: f)
: Yes, there are places where the colon is unnecessary. I've found it useful in test cases to have those --- sometimes for readability and sometimes to try and fool the parser.
Re: aside: I dunno --- I was born at the right time, and always had flashy whatsits (though I hear they have pills for that now). To your credit, I had to write some s-expressions to explain something on G+, and it just about made me throw the keyboard ('cept it was a laptop). Psychotic Bastard is bizarre (and seems more so the more I try and explain it to myself) but it's pleasant to write.
(Oh god, I just realized I've been ignoring infix precedence. Poop. I thought I had this worked out right finally.)
You're going to give :
a precedence between operators??? It's not an operator! It's a delimiter! I... ugh... arrgh. We gon' have words, friend.
Every possible method for doing if/else
is completely and utterly wrong and I despise each of them, individually, for the hateful snowflakes they truly are.
I had a code sketch for if
/else
that was a pretty naive 4 lines long. I put in a working but hideous version tonight via def-keyword
. It weighed in at around 50 lines, two of which were
; OMG HACKS
and
; END OMG
I'm taking this as a sign that I haven't got def-keyword
right yet.
I
For an
if
syntax using only normal magic colon, i'd go with(if pred then else)
, aliasthen
andelse
tobegin
/do
/progn
, which then allows all of the following forms:Biggest downside I see is adding another line to the x == 5 case's if block without adding a
then
.II
I agree,
def-colon
is wrong. It feels like a hack.My motivation is that PB started with the question "What should code look like?", and your if blocks and mine above all make me cringe (well, maybe not x == 9 above).
then
is unecessary when you have a colon after the condtion. Yeah,if
is a function, and yeah,if
can be passed around. In my head,if
/else
is a syntactic transform, such thattranslates directly to
For me the thing with
if
is that in s-expression land, it's a hack to do anything other than a 3 argument if, i.e.(if predicate do-this else-that)
. Implicit progn on the else case is a hack and so is arc style if. But if you do that, you get code that looks like this:or in PB:
This (along with unecessary parens and prefix operators and /their/ extra parens) was one of the three big pet peeves I had with reading and writing s-expressions: the else keyword massively improves the scannability of if statements.
So I'm pretty convinced there needs to be an
else
keyword and there needs to not be athen
keyword. There's no way this line of reasoning only applies toif
, so that leaves me at syntax-rules.(Haskell before Haskell2010 required elses to be indented relative to ifs in do notation (where indentation matters). There was much rejoicing when they allowed same line ifs.)
III
cond
gets ugly if you expect magic parens. The following two are equivalent:So you have to make sure to do:
This isn't fixed by
def-colon
either. You need stronger sauce or something crazy, like a third line terminator that does another kind of parenthesizing for that kind of thing, likefor
Or you can just be careful where you stick your parens.
IV
Re: lambda. I had been playing with
\x y:
as an alternate syntax forfn (x y)
, but having both in the language. If you only have one or the other, then yes, it has to be\ (x y):
, because you need to be able to go\ args:
. You've also hit on something I missed, that\x y:
doesn't play right with a generic unary operator rule and that a space is necessary between\
and the first argument.V
My use case for syntax rules beyond
if
is very nebulous. My original plan was to write up PB with magic colon, infix operators, and just enoughtdef-colon
to getif
/else
working, then see what I needed as I coded (I had expectations of wantingfoo.bar
being(foo 'bar)
for structures/namespaces/functions designed to look as such andfoo[bar]
being(foo bar)
for hash/array accesses and things designed to look that way.If I haven't convinced you about
if
/else
, I'd recommend just doing magic-colon and infix operators and doing the same test I've never gotten around to: trying it out to see how it feels.