matz / streem

prototype of stream based programming language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Streem examples

tbodt opened this issue · comments

So far, it seems that the only thing anybody knows how to write with Streem is the FizzBuzz and cat programs. Obviously, a language can't be that useful if it can only do FizzBuzz and cat.

Please comment on this issue with your examples of more useful things you can do with Streem. I still need to be convinced that there are any.

Keep in mind that Streem is in its really early phase of development. As far as I'm aware, not much is implemented yet besides parsing source files. There's still a bit of design discussion going on. That being said, Streem already has a few pretty cool conceptual ideas, like functional programming concepts and sending one stream to multiple consumers.

Heres an idea though:

// Get all usernames from a stream of input data. Assumes my seggestions of currying and prototypal are accepted, as well as my alternative syntax for lambdas.

['username': '', 'age': 0, 'init': (u, a) -> { this.u = u; this.a = a; }] -> User

def ith (n, thing) {
  thing[n]
} // Possible shorthand for: `def ith (n, thing) = thing[n]`, anyone?

ith(0, _) -> get_user

STDIN | split(',', _) | User.init(_, int(_)) | get_user | STDOUT
// The input format could be: Christopher Dumas,22

If it's premature to add a real-world example, what else?
Fibonacci? Prime Numbers? Or even Hello World?

Hello World

["Hello, world!"] | STDOUT

Fibonacci

def fib (n) {
  if n == 0 {
    0
  } else if n == 1 {
    1
  } else {
    fib(n-1) + fib(n-2)
  }
}

seq(10) | fib | STDOUT

This is a fairly naive example in terms of performance, but it should work (hopefully).

Edit: Whoops, this seems to be broken with the current streem parser. Any ideas as to what's wrong?

Currently, I think you can't write fib (n) {. Instead, fib (n) | {. But, this isn't what you'd like to do...

Is it seq(10) | fib | STDOUT. Or

seq(10) | { |x|
  fib(x)
} | STDOUT

and how do you give functions names. More examples, please.
Is it like in Haskell?

f x y = x * 2 + y * 2
g = f 2
h = g 3
h == 10

@blkdev Those should both work, Streem should have first class functions.

I added some examples in 2dce6a4. More to come.

@matz They are great examples. Until now, I didn't think of Streem as a language that could do TCP connections. 😎

@matz Cool! Out of curiousity, does "Hello World" | STDOUT work, or does the left side have to be a list?

@nicolasmccurdy it should be a list. I'd like to reserve | for bitwise or.

So is | going away for pipes?

Maybe we could use '>>' for pipes, and '%' for splitting them.

This example "05chat.strm" makes me confused:

broadcast = chan()
tcp_server(8008) | {|s|
  broadcast | s   # connect to broadcast channel
  s | broadcast   # broadcast incoming message
}

I'm not sure "broadcast | s" means data came from "s" to "broadcast" or came from "broadcast" to "s".

@Krysl s is a connection to a client. broadcast | s means data from broadcast will be sent to the client, and s | broadcast means data from the client will be sent to broadcast channel, then all clients connected.

@nicolasmccurdy I might confused you. I meant I wanted to reserve | (for scalar values, e.g. numbers and strings) for bitwise-or operation.

Another alternative is using the pipeline operator found in F# that being |> for pipes. I think the just adding the > sign in itself would make it much clearer in which direction things are being piped. So for example 05chat.strm would look like (with s renamed to client for clarity sake):

broadcast = chan()
tcp_server(8008) |> {|client|
  broadcast |> client   # connect to broadcast channel
  client |> broadcast   # broadcast incoming message
}

Or your could pipe it backwards too. I think this looks kinda confusing though:

broadcast = chan()
tcp_server(8008) |> {|client|
  broadcast |> client   # connect to broadcast channel
  broadcast <| client   # broadcast incoming message
}

Though I think as more on an architectural side note, I think this example could be confusing at first glance since its not explicitly clear how the broadcast |> client pipeline is to be executed whether it be:

  • Synchronously (block to completion) - Wait for broadcast to be completely exhausted before moving to the next line.
    In this case, the program would be stuck waiting for broadcast to completely finish piping everything to client. Since we would never know when broadcast would be completely finished in order to move to the next line, the program would be dead locked waiting when no data is going to be sent in the first place.
  • Asynchronously (run in the background aka "fire and forget") - Don't wait for the pipes to finish but continue running them in the background (the intended behavior)
    In this case, in the background, when we would get data from broadcast, we would pipe it to client and then we would get data from client, we would pipe it to broadcast. This would be the intended behavior since we essentially want to setup a background listener on either end.

I think its important to explicitly distinguish that we would rather always run pipelines in an asynchronous manner since it would lend to a concurrent design and you would not have to worry about a pipeline blocking another one especially if ran in an event loop or thread pool.

For example, the order of which the numbers in this example would get printed out is nondeterministic since both pipelines would be running concurrently (unless we added like a join or something between them before sending it to STDOUT):

seq(100) |> STDOUT
seq(100) |> { |x| x * 2 } |> STDOUT

Of course, individual pipes in a single pipeline would always run in a blocking fashion since you have to wait for input to work with (ex: in a |> b, b waits to execute until a sends something)

I think these are important concepts to think about going down the road either way.

👍 for |> and <|

See #18 comment for FizzBuzz in F# too for example.

How about named pipe?

mkfifo("/tmp/fifo", S_IFIFO|0666)
fifo_in = open("/tmp/fifo", O_WRONLY)
fifo_out = open("/tmp/fifo", O_RDONLY)
STDIN | fifo_in
seq(100) | fifo_in
fifo_out | STDOUT

And make it more simple, may like this

STDIN | [fifo] | STDOUT
seq(100) | fifo

And if a function can be bind to the named pipe, like a filter. 05chat.strm may look like this:

[broadcast] = chan()
[client] = tcp_server(8008)
broadcast | client | broadcast

[FIFO] may looks strange, we may need better symbols.
I'm a newbie, I wish this can be helpful.

@ChristopherDumas Agreed. That seems very clear to me, and it also looks kinda nice.

"It looks very cool in my opinion!" |> GITHUB

What would be nice is if there would be "cookbook examples" for streem too. Like we have fizzbuzz but what about other popular redirections? File stuff; also perhaps binary ... video, audio ... these things.

One could also do this via named pipes I suppose; just assign special functions to these pipes to act as filters (a bit similar how gstreamer handles source/sinks).

But matz also said that streem is experimental and it probably has lower priority than e. g. mruby so I guess this is stuff that may all happen "at a later time". :)

fibonacci

def fib (n) {
if (n == 0) {
0
} else if (n == 1) {
1
} else {
fib(n-1) + fib(n-2)
}
}

seq(10) | fib | stdout

Fibonacci

def fib (n) {
  if n == 0 {
    0
  } else if n == 1 {
    1
  } else {
    fib(n-1) + fib(n-2)
  }
}

seq(10) | fib | STDOUT

This is a fairly naive example in terms of performance, but it should work (hopefully).

Edit: Whoops, this seems to be broken with the current streem parser. Any ideas as to what's wrong?

fibonacci

def fib (n) {
if (n == 0) {
0
} else if (n == 1) {
1
} else {
fib(n-1) + fib(n-2)
}
}

seq(10) | fib | stdout

if add () is ok for now.