Hacker News new | ask | show | jobs
by a-saleh 3899 days ago
I don't think it makes that much sense w.r.t stream events:

1. most of the analytics/event-stream processing will be done on the backend anyway

2. If you are picky enough you can do functional programming in js. Reative.js is decent library for processing streams of events.

What screams to use something more haskell-like, is their example of 'Trys', i.e:

  class GraphQuery extends Query {
    static parse(object: any): Try<GraphQuery> {
      return TimeRange.parse(object.over).flatMap((timeRange: TimeRange) => {
        return Filter.parse(object.where).flatMap((filter: Option<Filter>) => {
          return GroupBy.parse(object.by).flatMap((groupBy: Option<GroupBy>) => {
            return new Success(new GraphQuery(
              filter,
              groupBy,
              timeRange
            ));
          });
        });
      });
    }
  }
should look like something like:

  parse : Any -> Try GraphQuery
  parse object = do 
    timeRange <- TimeRange.parse object.over
    filter    <- Filter.parse object.where
    groupBy   <- GroupBy.parse object.by
    return (createGraphQuery filter groupBy timeRange)
(haven't used haskell in quite some time, so maybe I am missing something)
4 comments

>should look like something like:

    >   parse : Any -> Try GraphQuery
    >   parse object = do 
    >     timeRange <- TimeRange.parse object.over
    >     filter    <- Filter.parse object.where
    >     groupBy   <- GroupBy.parse object.by
    >     return (createGraphQuery filter groupBy timeRange)
New ECMAScript 2015 Style

    parse = ({by, over, where}) => {
      timeRange = TimeRange.parse(over)
      filter    = Filter.parse(where)
      groupBy   = GroupBy.parse(by)
      return createGraphQuery(filter, groupBy, timeRange)
    }
or

    parse = ({by, over, where}) => 
      createGraphQuery(
        Filter.parse(where),
        GroupBy.parse(by),
        TimeRange.parse(over));
or

    parse = ({by, over, where}) => 
      createGraphQuery(
        Filter(where),
        GroupBy(by),
        TimeRange(over));
or

    parse = ({by, over, where}) => createGraphQuery( Filter(where), GroupBy(by), TimeRange(over) );
or

    parse = ({by, over, where}) => c(f(where), g(by), t(over));
and, if you're willing to put a front-end on it:

    parse = ({b, o, w}) => c(f(w), g(b), t(o));

Of course, I have not provided the definitions of `c`, `f`, `g`, or `t`.
This is slightly different from the original code. The reason for all the flatmapping is to avoid continuation if any of the subparsers fail, and notify the caller that something has failed because of {some exception}.

You can avoid excessive nesting/flatmapping with some syntax sugar, but it needs to be sufficiently different from normal declarative code.

I alluded to that in my last note. :)

If you properly define the functions (`c`, `f`, `g`, `t`), you can get the semantics of halted computation on failure. That is essentially what Haskell's `do` notation does.

You're absolutely right! I was hoping someone would point this out, since this is a much cleaner way of writing this code. This is pretty close to how it's done in Scala.

For the purposes of this article, we tried to avoid too much syntax sugar to make it more accessible. That aside, I'd love to add something like this to monapt![1].

[1] https://github.com/jiaweihli/monapt

Here’s a sketch of some potential syntax to simplify your TypeScript so it’s like that Haskell code.

  class GraphQuery extends Query {
    static parse(object: any): Try<GraphQuery> {
      return TimeRange.parse(object.over)
        .combinedWith(() => Filter.parse(object.where) )
        .combinedWith(() => GroupBy.parse(object.by) )
        .withCombinedValues((timeRange, filter, groupBy) => {
          return new Success(new GraphQuery(filter, groupBy, timeRange));
        });
    }
  }
In this design, `combinedWith` is a lot like `then` when using promises. It’s a bit more complicated because `then` assumes that arguments are always passed directly from the previous function, so you need special support to store the return value and retrieve them later as arguments.

If the type system requires it for some reason, you could add a `.startCombining()` call at the end of the first line within the method, so that `combinedWith` is sure to be possible.

You can build UIs as a left fold over events, just as you can build backend data processing pipelines. In fact it that is the architecture that makes the most sense to me now. I have a little toy framework here that demos these ideas. https://github.com/boothead/oHm
May be you can funscript? http://funscript.info/ It leverages TypeScript metadata to do typed interop with JavaScript libraries through an F# type provider