dimitriv / Ziria

A domain-specific-language and compiler for low-level bitstream processing.
92 stars 18 forks source link

Issue parsing seq block with a single take #53

Closed bradunov closed 9 years ago

bradunov commented 9 years ago

Hi Edsko,

this used to parse (code/WiF/testbed/rx.blk, you can test both make rx.out and tx.out for parsing):

fun comp skip() { times 1 { x<- take; };

repeat { x<- take; emit x; }
} in

with error: rx.blk "rx.blk" (line 104, column 3): unexpected "}" expecting computation last statement in a seq block should be a computation

pointing to the brace after x<-take in times. I discovered that everything is fine if I remove x. Is x<-take not a computation? Is it like a let bound? Is there a way to make this error message a bit more clear? (I am not sure, thus only a discussion flag)

dimitriv commented 9 years ago

I personally find "last statement in a seq block should be a computation" quite clear. How can we make this clearer? Any suggestions?

bradunov commented 9 years ago

take is a computation. So why is x <- take not a computation? I always assumed it is a computation but apparently it is not (because adding emit x after it compiles). Maybe I don't understand what is actually wrong here.

valderman commented 9 years ago

GHC does this because do x <- a ; b desugars to a >>= \x -> b, but I don't think there's any technical reason to do the same (except maybe to make compiling seq slightly less complex?) - couldn't we just throw away the x and compile x <- a as if it were just a when it occurs at the end of a seq, and maybe give the programmer a warning about a variable being thrown away?

gstew5 commented 9 years ago

The programmer can always write: x <- take; return (), or whatever the current syntax is for returning unit. Is there a good reason to add special desugaring for this particular x <- take pattern?

valderman commented 9 years ago

I guess that depends on just how confused and/or annoyed radio people get by not being able to end a computation with x <- blah. Not really, probably.

dimitriv commented 9 years ago

As others said before there is no well-formed x <- comp syntax in our language and there never was. comp is a computation above, x <- comp is nothing. Not well formed. Just a piece of text. Bind always has 3 parts, the thing we compute first, the variable we bind, and the continuation seq { x <- c1; c2}. Think in terms of the graphical composition along the control channel. What sense does it make without any block to switch over to?

Moreover, I don't find any great reason for auto-completing incomplete bindings like this with return (). Suppose we have:

   seq { c1; c2; c3; comp } 

And then the programmer modifies this to:

   seq{ c1; c2; c3; x <- comp } 

His program will not type check any more. So maybe the auto-completion to return () is not the right one, maybe we should be auto-completing x <- comp to seq { x <- comp; return x}. Who knows? It's pretty arbitrary. My personal preference is that we resolve this issue by not allowing such incomplete bindings.

dimitriv commented 9 years ago

Actually @bradunov, if you look at @valderman's comment above he suggests the alternative typing rule and translation of an incomplete binding x <- comp to just comp, which has a different type than the auto-completion that we do today (which is to just return ()). So the ambiguity issue is very real and I stand firm behind my "let's disallow this" proposal.

bradunov commented 9 years ago

I am fine with the correctness w.r.t. the rules we set up, but I believe it is confusing. So: seq { x <- comp1; comp2} runs comp1, then switches to comp2 and ignores x; seq { comp1; comp2} runs comp1, then switches to comp2, again ignoring the potential output of comp1. So why wouldn't seq{x <- comp1} just run comp1 and ignore x, as in the above cases. I think this is not about radio guys, this is more fundamentally a difference between functional and non-functional programmers. Consider let comp f() = {take} in ... A non-functional programmers would understand this as f() = {x<- take; return ()} (because there is no explicit return) and a functional programmer would I guess understand this as f() = {x <- take; return x} (because it equals the two "functions"). I am slightly more inclined to cater for the engineers, which belong to the first category, but I am really also fine with what you all seem to agree on. But then I would insist to make the error message much more clear (as it took me a good 5 mins to figure out what it was about).

edsko commented 9 years ago

A block cannot end on x <- foo; this is not because of compiler implementation reasons, but it just doesn't make sense from a semantic perspective. We cannot just "throw the x away", because it's not x that is the problem, but <-. After all, <- is not an assignment! @dimitriv says it best:

Think in terms of the graphical composition along the control channel. What sense does it make without any block to switch over to?

I've modified the parser so that it maintains a bit more information so that it can give a clearer error message. Given

fun comp f() {
  x <- take; 
}

it will now give the error message

unexpected symbol "}"
expecting command
A block must end on a computation, it cannot end on a bind.
Try removing the 'x <-' part perhaps?

It hope that's clearer. Closing this issue now; if there are specific ways the error message should be improved, feel free to reopen.