Live data from Hacker News

Streem – a new programming language from Matz

github.com

191–199 of 199 posts

Re: Streem – a new programming language from Matz

#191
post #87
post #67

Earlier quoted context omitted.

In Elixir, |> does not flip arguments. It lets you chain together multiple functions by inserting the result of the previous function as the first argument of the following function. Here's an example from http://www.theerlangelist.com/2014/01/why-elixir.html The following code computes the sum of squares of all positive numbers of a list: list |> Enum.filter(&(&1 > 0)) |> Enum.map(&(&1 * &1)) |> Enum.reduce(0, &(&1…

F# doesn't flip arguments either. It's an operator: let (|>) x f = f x Is basically saying: x |> f is equal to f x So in F# it's the same as Elixir, but the value x is applied to the function f (passed as the last argument). i.e. list |> List.filter (fun x -> x > 0) |> List.map (fun x -> x * x) |> List.reduce (fun s x -> s + x)

I was going off what platz said in another comment, that |> flips the arguments to |> in the same way Haskells flip function does, which I thought the type signature of the the F# |> also indicated. I'm sorry if I'm misunderstanding things.

I think the difference though is that the |> in Elixir is actually a macro that modifies the following function call's first argument.

So list |> Enum.filter(&(&1 > 0)) doesn't end up using filter as a curried function as one would find in Haskell:

Enum.filter(&(&1 > 0)) list

The end result is actually:

Enum.filter(list, &(&1 > 0))

Re: Streem – a new programming language from Matz

#192
post #128

Earlier quoted context omitted.

I think I don't believe you. Look at the car and house example above. Is that arcane?

Well, sorry but sed _is_ cryptic. Let me quote an exemple for you (squeezing blank lines): >leaves a blank line at the beginning and end if there are some already. #!/usr/bin/sed -f # on empty lines, join with next # Note there is a star in the regexp :x /^\n*$/ { N bx } # now, squeeze all '\n', this can be also done by: # s/^\(\n\)*/\1/ s/\n*/\ / As soon as you begin to use sed registers, the code becomes arcanic.

> I think that stream-oriented languages are doomed to have an arcanic syntax.

> Streams are a non-trivial construction, after all.

...

> Well, sorry but sed _is_ cryptic. Let me quote an exemple for you (squeezing blank lines):

I agree with you that sed is cryptic, but I don't think that that necessarily means that stream processing languages are doomed to have an archaic syntax. I'd also agree with marvy in saying that the car and house example is very clear as to it's intent:

  STDIN | /\w+/{|word|
      /house/ {
        # when word is house
      }
      /car/ {
        # when word is car
      }
      {
        # default case
      }
    }
I'd say that's very straightforward and not arcane at all. Just because sed is arcane (it could be argued) doesn't make it a precondition for any stream language

EDITS: formatting.

Re: Streem – a new programming language from Matz

#193
post #161

Earlier quoted context omitted.

> In my example, Scala interprets "ary.map(f(_ * 2))" as "ary.map(f(x => x * 2))" I think this is where I'm not communicating what I'm trying to say very well. What I'm trying to say is that that statement is false. The underscore isn't a placeholder saying "inject a Function here". You probably didn't mean that exactly of course, but it's a programming language; it pays to be a bit pedantic I think. It's easier to u…

> The underscore isn't a placeholder saying "inject a Function here". It is . Look at the error message: error: missing parameter type for expanded function ((x$1) => x$1.$times(2)) It did inject a function, it's right there, in plain text. Then it did type inference. I mean, what else is "lifting" the asterisk to a Function supposed to mean, if not injecting a function around a placeholder? And what about "1 + _ * 2…

> It did inject a function, it's right there, in plain text. Then it did type inference.

Yes, the inferencer/type-resolver runs after the parser (there's a recent thread on the ML about a Parboiled parser you might find interesting BTW). My point was this is parsed. It's an AST at that point, it just hasn't resolved the types yet. It's not a source preprocessor. To me that implies limitations, but also an expectation of consistency.

> ... what about "1 + _ * 2"? What is it lifting? The asterisk? No. It is lifting more than that.

Yes! Paste your code into the REPL and see what you get:

  scala> 1 + _ * 2
  :8: error: missing parameter type for expanded function ((x$1) => 1.$plus(x$1.$times(2)))
              1 + _ * 2
This is a real hairy example IMO since it's conflating the mechanics with optional parenthesis and dot-less method calls, but we can break it down just the same. Since Int.+ takes a single argument, then it must parse as:

  1.+(_ * 2)
NOT

  (1 + _) * 2
The first example is trying to lift $times from something. The second is trying to lift the instance-method that's already bound to 1:Int to a Function0[Int].

They're doing exactly the opposite thing right? It's like assuming that the LHS and RHS of an assignment are interchangeable. They're not. One is a label and one is a value.

> how much of the context does it grab along with it?

None. In this position, it's the beginning of an expression. It doesn't escape the scope it's defined in. Just like in the 1.+(_ * 2) example, what comes before it doesn't matter. It's the beginning of an expression.

> I still don't see what type inference has to do with this.

I probably just complicated things bringing it up. ary.map(_ * 2) vs ary.map((_:Int) * 2). I feel like maybe it's not understood that those do the same thing. The only difference is the type is inferred from the signature of map[T] (simplified minus CBF) on the first.

I'm not trying to debate that sometimes it's frustrating that I can't just:

  documents foreach(couchdbActor ! Queue(json.merge(_)))
Intuitively, it seems like it's possible it could work. And it might if the underscore was accomplished through code-rewriting of some sort right? But it's not. So what I'm doing is:

  documents foreach(couchdbActor.tell(Queue(JsObject => JsObject))
I'm not satisfying the signature of foreach because tell() isn't returning a Function[JsObject,Nothing], and I'm passing a Function[JsObject,JsObject] to Queue.apply (case class Queue(payload:JsObject)). Neither of those things makes sense. So what I do instead:

  documents foreach((couchdbActor ! _) compose Queue compose json.merge)
Breaking that down, I've got:

  (couchdbActor ! _) // This just lifts the tell method, which gives me a Function[Any,Unit].

  Queue.apply // Function[JsObject,Queue]: I can compose that with my lifted tell to get a Function[JsObject,Unit]

  json.merge // Function[JsObject,JsObject]: Doesn't change the signature: Function[JsObject,Unit]
Now foreach() gets passed a Function[JsObject,Unit]. Which is exactly what it's expecting.

When you break it down, you have to keep a "stack" in your head. Which can get hard sometimes (at least for me), but the underlying consistency about what's actually happening makes it simple to do. It's like a basic addition problem:

  27 + 34 + 192 + 11
I expect for most people that takes some deliberate thought, breaking it into smaller operations, carrying the result in your head as you go. But it's still simple, because it's consistent.

> In a dynamic language, you would stop at the lift, but it would otherwise work just the same.

I was going to attempt to write a Ruby analogue, but I can't actually figure out a clean way to do that.

I dunno. I feel like you probably understand all this just fine. You just don't like it. You'd prefer a sort of source-rewriting approach? So maybe Scala just isn't the language for you. I feel like once you understand the mechanics of the underscore, it's pretty straight-forward. Similar to beginners getting tripped up on for-comprehensions:

  val o = Option(1)
  val f = Future.successful(List(2,3,4))

  for {
    n 
That trips up everybody at some point right? If you understand that it's just map, flatten and filter, it's clear why that doesn't work though. How do you say: Option(Future(2)).flatten? What would that do? Can't work. It may be initially frustrating for new users, but it's consistent. If you study the mechanics, then it becomes second nature. The mystery disappears. That's really all I was trying to do here. Hopefully someone finds it useful.

Side note: This is a big part of why I find for-comprehensions mostly useless. They do nothing you can't already do, and they introduce what can look like magic. They're pure sugar, except more often than not, they're also longer to write. Plus they're mostly useless outside of testing. How often do you want a Failure to just throw? Or a Play Action to return a None? More often than not, you're gonna want to fold(), getOrElse(), match { case None => NotFound; case Some(foo) => ... Ok(finalResult) } etc. But I digress. :-)

Re: Streem – a new programming language from Matz

#194
post #3

Why do most implementations of FizzBuzz special case % 15? I haven't ever really understood this. Maybe it's just my math-y background, but it always seemed to me you should just check mod 3 and mod 5 without an else between them, concatenating Fizz and Buzz. Can anyone else comment on this? Most canonical FizzBuzz programs special case 15, and I don't get it.

Because of the requirement to print the number if it is divisible by neither. Here:

    if x isDivisibleBy 3: print "Fizz"
    if x isDivisibleBy 5: print "Buzz"
and.. how do I now print x in the neither case? I can't 'else'. I could make a long if not divisible by either if expression, but that's less easy to read than an if/else chain that starts out with 'if divisible by both, print fizzbuzz'.

If fizzbuzz was: Print FizzBuzz for multiples of 15, fizz for multiples of 3, and buzz for multiples of 5, and nothing otherwise, I bet you'd see the above pseudocode far more.

Re: Streem – a new programming language from Matz

#195
post #117

Earlier quoted context omitted.

Whether you print the number, Fizz, Buzz or FizzBuzz you are going output a line break, so I'm not sure what you would be checking for. Output \n unconditionally.

Then you call `printf` twice every loop instead of once. `printf` is buffered so you aren't making two system calls, but you are still making two function calls.

Maybe it's a performance/brevity compromise, but the latter is the issue the I addressed. The least-calls solution would probably be to print the whole output as a single string constant.

Re: Streem – a new programming language from Matz

#196
post #139
post #26

Earlier quoted context omitted.

It's not any shorter. The extra check as to whether or not to print the line break cancels out the mod 15 check. In my opinion, it's cleaner to have three conditionals of the same type than two checking mods and a third checking the OR of the first two. Of course, it can be actually shorter with a goto.

In some languages you can hide the extra check: for i in range(1,100):print("Fizz" (i%3==0) + "Buzz" (i%5==0) or i)

For others who are confused by this syntax, it appears that there are *'s that got eaten and turned into italics.

Re: Streem – a new programming language from Matz

#197
post #193

Earlier quoted context omitted.

> The underscore isn't a placeholder saying "inject a Function here". It is . Look at the error message: error: missing parameter type for expanded function ((x$1) => x$1.$times(2)) It did inject a function, it's right there, in plain text. Then it did type inference. I mean, what else is "lifting" the asterisk to a Function supposed to mean, if not injecting a function around a placeholder? And what about "1 + _ * 2…

> It did inject a function, it's right there, in plain text. Then it did type inference. Yes, the inferencer/type-resolver runs after the parser (there's a recent thread on the ML about a Parboiled parser you might find interesting BTW). My point was this is parsed . It's an AST at that point, it just hasn't resolved the types yet. It's not a source preprocessor . To me that implies limitations, but also an expectati…

> This is a real hairy example IMO since it's conflating the mechanics with optional parenthesis and dot-less method calls, but we can break it down just the same. Since Int.+ takes a single argument, then it must parse as:

> 1.+(_ * 2)

No, my point is that this is not how it parses. Look:

    ary.map(1 + (_:Int) * 2)
    res3: Array[Int] = Array(3, 5, 7, 9, 11)
    ary.map(1.+((_:Int) * 2))
    error: overloaded method value + with alternatives:
      (Double)Double
      ...
In the first case, the parser lifts _ over $times AND over $plus. The function is being lifted over both of them. The optional parentheses break the feature if you insert them: "1.+(_ * 2)" tries to add one to a function, which is not a valid operation.

>> how much of the context does it grab along with it?

> None. In this position, it's the beginning of an expression. It doesn't escape the scope it's defined in. Just like in the 1.+(_ * 2) example, what comes before it doesn't matter. It's the beginning of an expression.

If that was true, then "1 + _ * 2" would be invalid, and "f(_)(1)" would be equivalent to "f(1)". But the former is valid and the latter doesn't work like that.

> I was going to attempt to write a Ruby analogue, but I can't actually figure out a clean way to do that.

There is a limited Python analogue:

https://github.com/kachayev/fn.py#scala-style-lambdas-defini...

That only works insofar that the _ placeholder object can seize control over the expression, so "f(_, 2)" won't work unless f explicitly handles _. Implementing the full feature would require language or macro support, but my point was more that a new dynamic language could trivially support it.

Re: Streem – a new programming language from Matz

#198
post #93

Earlier quoted context omitted.

Personally I like it and find it clear. Compare: ary.map(_ * 2) ary.map(x => x * 2) "_" is perhaps an ugly choice of character (frankly I'm not sure why Scala is so obsessed with it, since it's used for so many featurse), but I think the semantics are sensible. Of course, in a more functional language you could perhaps just write ary.map(* 2)

What about a middle ground on swift's syntax? Arr.map { $1 * 2 }

Yeah, that'd be pretty cool.

Re: Streem – a new programming language from Matz

#199

I love Ruby and I love Matz. With that being said there are some things that Ruby struggles with. I know that there have been some conversations among the core on bringing in more functional concepts to Ruby....at least since April. To me this says that Matz is coming to the conclusion that we may need a new language to get functional right. While I am sad to see that Ruby may be superseded by a new language I'm real…

> "Matz is nice and therefore we are nice" This non sequitur annoys me. Deconstructing it: * A: "Matz is nice": Let's say we all agree this is true. * B: "we are nice": i.e., the ruby community is nice. * P(A -> B): (A therefore B) is a slogan, so I assume the proposition P is believed to be true. Is it? In order for P to be true, the only option is for B to be true as long as Matz keeps being nice. Assuming Matz is…

I think the intent was more along the lines of "Matz is nice and therefore we should be nice", but in making it shorter and snappier it now uses a more ambiguous piece of the English language.
Post reply on HN