19:29
<jschoi>
I don't know. F# computation expressions don't feel as fluid to me as FLWOR/FLWOS syntax, and the ability to introduce arbitrary "keywords" inside such a block isn't great for static analysis. That said, being able to add a distinct or count in a F# query expression is an improvement over C#'s LINQ syntax

With regards to static analysis, I don’t think there would be a fundamental problem with static analysis of context blocks beyond having to analyze decorated constructs in general. As long as the analyzer knows the identity of a decorator, then it knows the types of the callbacks that are involved in its decorated constructs.

But it’s true that F# computation expressions are slightly more verbose than bespoke LINQ FLOWR/FLOWS syntax. That’s the price of its being generic.

19:35
<rbuckton>

With regards to static analysis, I don’t think there would be a fundamental problem with static analysis of context blocks beyond having to analyze decorated constructs in general. As long as the analyzer knows the identity of a decorator, then it knows the types of the callbacks that are involved in its decorated constructs.

But it’s true that F# computation expressions are slightly more verbose than bespoke LINQ FLOWR/FLOWS syntax. That’s the price of its being generic.

It depends to what level you want to support F#-style computations. F# allows custom keywords using a [<CustomOperation("name")>] attribute and you can specify options in that attribute that affect parsing, such as AllowIntoPattern, IsLikeJoin, MaintainsVariableSpace, JoinConditionWord, etc. Since JS decorators are evaluated at runtime, the engine becomes limited in what static analysis it can do for compilation.
19:39
<rbuckton>

i.e., in F# you can do:

query {
  for student in db.Student do
  groupBy student.Age into g
  select (g.Key, g.Count())
}

Which handles into based on whether AllowIntoPattern is set on the groupBy method of the builder.

Or

query {
  for student in db.Student do
  join selection in db.CourseSelection
    on (student.StudentID = selection.StudentID)
  select (student, selection)
}

Which parses on as the join condition because it's specified by JoinConditionWord.

19:44
<rbuckton>

F# made this much more customizable than C#, which has explicit syntax for group, join, into, on, select, etc. To do this in JS could mean parsing arbitrary patterns of identifiers and expressions and then enforcing them after the fact:

CoverComputationJoinLikeStatement:
  Identifier BindingIdentifier `in` LeftHandSideExpression Identifier LeftHandSideExpression

Where the first Identifier has to match a custom operation keyword, and the 2nd Identifier has to match that operation's JoinConditionWord. Its a level of complexity that I think many would balk at

19:51
<rbuckton>

Plus I'm not sure this would fit well with existing JS expressions/statements...

@query do {
  for (const student of db.Student) {
    join (const selection of db.Selection on student.studentId === selection.studentId) {
      select #[student, selection];
    }
  }
}

// or
@query do {
  for (const student of db.Student)
  join (const selection of db.Selection on student.studentId === selection.studentId)
  select #[student, selection]
}
20:18
<jschoi>

F# made this much more customizable than C#, which has explicit syntax for group, join, into, on, select, etc. To do this in JS could mean parsing arbitrary patterns of identifiers and expressions and then enforcing them after the fact:

CoverComputationJoinLikeStatement:
  Identifier BindingIdentifier `in` LeftHandSideExpression Identifier LeftHandSideExpression

Where the first Identifier has to match a custom operation keyword, and the 2nd Identifier has to match that operation's JoinConditionWord. Its a level of complexity that I think many would balk at

I suppose—if we did do custom keywords in context blocks, and that’s a big if—we could also force them to use a sigil prefix too.
20:19
<jschoi>
I think that LINQ blocks probably could still be usable without custom keywords anyway.
20:23
<rbuckton>
I have a hard time justifying the value without features like join, group, orderby, etc.