matz / streem

prototype of stream based programming language
MIT License
4.6k stars 236 forks source link

The block parameter syntax ("|x|") might be misinterpreted as "a pipe through x"? #25

Closed practicalswift closed 9 years ago

practicalswift commented 9 years ago

The similarity between the flow operator (|) and the block parameter syntax (|x|) will probably be a source of some confusion since |x| can be misinterpreted as "a pipe through x".

The problem is not the flow operator (|). On the contrary – using the pipe as the flow operator conveys the meaning very intuitively for everyone who has ever used a shell, so let's keep that very good choice.

Instead I suggest switching from |x| to say (x) ->, (x) or x -> to denote block parameters.

The (…) -> alternative is implemented here: https://github.com/practicalswift/streem/commit/193fa8413d14dce03d609ec597006a40ca678ec1

Reasoning:

Summary of discussed alternatives:

Alternative 1 (current syntax).

seq(100) | { |x|
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

Alternative 2 (implemented here: https://github.com/practicalswift/streem/commit/193fa8413d14dce03d609ec597006a40ca678ec1).

seq(100) | { (x) ->
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

Alternative 3.

seq(100) | { (x)
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

Alternative 4.

seq(100) | { x ->
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT
alexispurslane commented 9 years ago

:thumbsup: for Alt. 4! Heres another idea: Alternative 5:

seq(100) | (x) -> {
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT
nickserv commented 9 years ago

I agree, I think it's a lot more clear without |x|. I think I like alternatives 2, 4, and 5 the most so far.

hhff commented 9 years ago

:+1: for Alternatives 2 & 5

caseycole589 commented 9 years ago

I vote 1 & 3

On Mon, Dec 15, 2014 at 4:34 PM, Hugh Francis notifications@github.com wrote:

[image: :+1:] for Alternatives 2 & 5

— Reply to this email directly or view it on GitHub https://github.com/matz/streem/issues/25#issuecomment-67079436.

ekg commented 9 years ago

I don't understand the { |x| ... } syntax. Should we understand this to be an anonymous (stream-oriented) function? If so, the syntax for function definition, anonymous function definition, and this example should be harmonized.

acangiano commented 9 years ago

3 is the most ruby-like among the alternatives. I like it.

matz commented 9 years ago

I feel same way, and agree to change. But I am afraid that alternatives in OP cause shift/reduce conflict. I am thinking of ->x{...} as function objects/blocks.

FYI, op_rasgn is not mandatory. we can remove it.

tbodt commented 9 years ago

If op_rasgn was removed, how would you use variables? (I don't think you can have a programming language without variables.)

matz commented 9 years ago

you can assign using =, e.g. s = socket.accept(), instead of socket.accept() -> s.

alexispurslane commented 9 years ago

@acangiano why are we trying to be like Ruby? Sure, Matz designed ruby, but that doesn't mean everything that has anything to do with him should look like Ruby. Also, I am in favor of keeping the op_rasgn for assignments, as that is in keeping with the stream-oriented idea. So how about another alternative? Alt. 6:

seq(100) | (x) => {
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

That seems still in keeping with most other languages, (including CoffeeScript and ES6), and wouldn't lead to any shift/reduce conflicts. Also, I do thing that #26 would be something to think about as far as shorthand for transforming and filtering streams.

practicalswift commented 9 years ago

What about simplifying the syntax further and introducing shorthand argument names $0, $1, $2, etc:

Alternative 7.

seq(100) | {
  if $0 % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

foo | {
  zip($0, $1)
} | STDOUT

Reasoning:

In cases where argument names are needed for readability the syntax would be a slightly modified version of alternative 3 above to avoid the shift/reduce conflict that @matz mentioned in his comment:

Alternative 8.

seq(100) | (x) {
  if x % 15 == 0 {
    "FizzBuzz"
  }
...
} | STDOUT

foo | (arg1, arg2) {
  zip(arg1, arg2)
} | STDOUT

Reasoning:

So I suggest to allow for BOTH alternative #7 and alternative #8 depending on if named arguments are required or not :-)

shouya commented 9 years ago

I like the haskell-like lambda syntax:

seq 1 100 | \x -> length x | print
seq 1 100 | lines | map (\x -> length x) | fold sum | print

Of course if currying is good supported, \x -> length x can be simplified into a sole length. Pretty neat I consider this syntax :)

If a block is wanted, we could take

seq 1 100 | \x -> {
   do something...
} | print

Probably alternative #9? Actually it's just similar to alternative #5.

Krysl commented 9 years ago

I don‘t like (arg) to be outside of the {…}. because if I want to write some codes to calculate 1/1+1/2+…+1/10 and print sum at last like this:

seq(10) | (x) {
  sum += 1/(x)
  {
    print sum
  }
}

It looks like a num divided by a function! At last,I give a alternative too:

seq(100) | { (x => f b)
  if x % 3 == 0 {
    f = x
  }
  else if x % 5 == 0 {
    b = x
  }
} | { (x y) 
  printf("total fizz:%d\n", count(x))
  printf("total buzz:%d\n", count(y))
}
mitchellmcmillan commented 9 years ago

@Krysl please edit your comment to mark your code as a code block to make it more readable

haiyuofow commented 9 years ago

I like alternative 3

tbodt commented 9 years ago

:+1: alternative 5

mySingleLive commented 9 years ago

seq(100) | #(x) { | x % 3 == 0 --> f = x | x % 5 == 0 --> b = x } | print

mySingleLive commented 9 years ago
seq(100) | #{
   | Odd --> f = x
   | Even --> b = x
} | print
mySingleLive commented 9 years ago

Perhaps the closure parameter can be omitted when there is only one argument

dbohdan commented 9 years ago

Alternative 7 can be implemented alongside any other block parameter syntax for when no block parameters are explicitly declared, kind of like Clojure's compact lambda syntax. It would be nice for one-liners. The downside is that it makes a class of typos hard to detect, e.g., if you type $1 instead of $0 on accident how can the interpreter tell if you meant it? For this reason you'd want a more explicit syntax to be there as well for longer scripts.

alexispurslane commented 9 years ago

:+1: Alternative 7 & 5 together. Like in Clojure.

nickserv commented 9 years ago

I liked @tbodt's idea of creating a table of votes in #24, so I'll add one for the alternatives here:

Alternative Votes Syntax Note
1 2 { |x| … } current syntax
2 3 { (x) -> … }
3 4 { (x) … }
4 2 { x -> … }
5 5 (x) -> { … }
6 1 (x) => { … }
7 2 { } parameters are $0, $1, $2, etc.
8 1 (x) { … }
9 1 \x -> { … }

Last updated: this comment.

Sorry if I excluded anyone, I tried to only include votes that were more explicit, and only alternatives that were numbered to make things simpler.

nickserv commented 9 years ago

By the way, in the future, it might be easier to use some sort of wiki and/or poll site to organize alternative ideas. http://poll.gitrun.com seems interesting.

vtenfys commented 9 years ago

I like alternative 3

matz commented 9 years ago

I am afraid alternatives 2,3,5,6,8 will cause shift-reduce conflict. I like alternative 10, which is

-> x { ... }

similar to Ruby's lambda.

alexispurslane commented 9 years ago

That might be confusing if you want to assign a lambda to a variable. Alt. 6, 8, and 4 are the least likely to cause conflicts.

practicalswift commented 9 years ago

@matz Let's say you decide to go with alternative 10, would you consider also having alternative 7 available? As I see it those should not be mutually exclusive, and I think alternative 7 is proper for quick Streem scripts (c.f. shell scripts) where the number of parameters are few and naming them feels overkill.

Just as the meaning | is immediately obvious to everyone with shell experience (which should be more or less all programmers), I think the numbered arguments ($0, $1, etc.) in alternative 7 has the same intuitive appeal for anyone with shell scripting experience :-)

My suggestion:

s-aida commented 9 years ago

From a pure standpoint of appearance, I don't like Ruby's lambda syntax at all. Most modern languages go with something like (x) -> {}, and every time I return to write codes in Ruby, the very syntax feels like pretty weird.

So big :-1: 10, but :+1: 5 (6 is also okay).

Or, once again, the parser of Streem is supposed to be so complicated and chaotic that you can't implement a standard syntax?

matz commented 9 years ago

@shunsukeaida I don't know how other language works (yet), but when the parser see

(a)->{...}

it's quite difficult for the parser to distinguish (a)-> (beginning of a function object) and (a) (a variable surrounded by parentheses), without shift-reduce conflicts. Other parsers may do precedence tricks, or ignore conflicts.

alexispurslane commented 9 years ago

Alt. 6 is very similar to Alt. 5. Maybe that's what we should go with.

spuk- commented 9 years ago

Anyone here knows Microsoft Powershell (http://technet.microsoft.com/en-us/library/hh847902.aspx - "about_Pipelines")? Streem immediately reminds me of it.

In Powershell, objects sent thru the pipeline get assigned to '$_' by default, which would be like option 7, but a single parameter (not sure if you can have more than 1 object sent thru at a time), which is good for quick scripting/oneliners, but when declaring functions you can assign them to named variables (in a more bureaucratic way than most *nixers are probably used to).

Anyway, I personally don't fancy the little arrows, typing '()' or '||' requires less finger traveling.

So I'd like 7 (by default) plus 3.

Also I'd like to note this looks to me like a language/shell for sysadmins, not necessarily full blown programmers. :]

britishtea commented 9 years ago

{ <a, b> ... } or <a, b> { ... } cannot be confused with pipes.