As long as people are voting, I'm very much in favor of removing the requirement for parens around method calls and property access specified via the dot notation. This trips me up constantly and just feels odd.

The other two changes are of less importance, but would be welcome none-the-less. I'm already mostly used to .[] for Item access, so that's not a huge deal. However, while making white space significant for binding an operator as either monadic or dyadic sounds pretty cool, what would be the breakage cost to existing code? Assuming these both become enabled only with the #light syntax (which I love by the way), then the risk should be pretty low and I'm all in favor.

-- James

By on 10/15/2007 2:07 PM ()

BTW, my vote for the second most annoying syntax oddity goes to the issue of having to use .get_Item(x) instead of .[x] whenever the indexer is overloaded.

By on 10/15/2007 2:32 PM ()

Yes, we'll fix this one for sure :-)

For the record it stems from a heroic but ultimately flawed attempt to maintain perfect OCaml compatibility for OCaml code using x.[y] as string lookup. We managed to get OCaml compat here (by defaulting the lookup to work on strings), but the error messages resulting from under-resolved uses of the operator were poor. Hence we abandoned the default-to-string rule in the 1.9.x releases and now a type annotation is needed to resolve the use of the operator.

We do care about OCaml compat, as you can see from the above.

Kind regards

don

By on 10/15/2007 2:50 PM ()

According to the language specification "f(x)" binds tighter than "f x", though not in nested function applications like "f g(x)". Personally, I would favor if there was no exceptions to the precedence rule so that one could get rid of a lot of parentheses

[I replied to the e-mail of this post, but apparently that is not a way to post to the list. Since this conversation has continued, here is what I said yesterday]:

Interesting. I am strongly aligned with the notion that the form a b c binds as a(b(c)) in applicative systems, although it is not the combinatory-logic tradition. Having bracketed forms bind properly in terms of conventional notation involving conventional types is very useful, including in forms such as f g[i,j] s(z). Because I have taught myself this, and how to use it economically (including niceties such as f(a, b) for f(a) b for (f a) b) and a different notation for tuples versus applicative syntax, the argument is very appealing for me.

However, I am not sure how we find ourselves voting about something like this at this point in F# development. It has me want to be sure I don't invest any significant effort in building production code with it at this point in time. (Truthfully, I don't have that option for any current projects anyhow, but this has me be wary of F# as an interesting language for novices and apprentice cybersmiths too, while it remains in experimental language-design status.)

- Dennis

By on 9/14/2007 9:20 AM ()

Hi Stephan,

I quite like this idea. We were pleased with how the initial adjsutment to the precedence rules worked out, and the limitation we mentioned above was just a conservative restriction we placed in order to achieve the basic goal of allowing

1
   a.M().N().P

etc.

There is one other place where people are frequently requesting that placing tokens lexically next to each other should change precedence and semantic meaning: array/dictionary lookup. For example,

expr[expr]

could be subject to a similar rule, so people can use "arr[3]" and "dict[3]" instead of "arr.[3]". This is a very common request and I understand why. However

1
   f x y [] 4

Would parse as an application, and here "[]" is the empty list.

Finally, Jon Harrop has previously suggested that

1
   f -x -y -z

would parse the "-" symbols as applications of unary negation, based again on a rule that says whitespace is significant.

I think all of these are actually reasonable and while complicating the languagte spec marginally, I suspect they would not actually be confusing to users in practice (indeed the opposite)

Kind regards

Don

By on 9/14/2007 7:37 AM ()

[The] limitation we mentioned above was just a conservative restriction we placed in order to achieve the basic goal of allowing

1
a.M().N().P

Yes, interesting challenge when you have special infix and other-fix operators. But I think the precedence works properly in the case of "." as a method introducer. You want a.b.c()() to be ((a.b.c)())() and a.b().N to be (a.b()).N, seems to me. It is fun doing the BNF for this, but I think it is still operator precedence (. has higher precedence on the left of ( and lower precedence on the right of ) or some such.)

There is one other place where people are frequently requesting that placing tokens lexically next to each other should change precedence and semantic meaning: array/dictionary lookup. For example,

1
expr[expr]

could be subject to a similar rule, so people can use "arr[3]" and "dict[3]" instead of "arr.[3]". This is a very common request and I understand why.

An interesting historical factoid here is that Brooks and Iverson did this in the use of APL notation in their book on the language and the architecture of computer systems before there were any public implementations of APL systems. Since this was a kind of reference language or publication language usage, subscripts were set in subscript form, so it was obvious why the association needed to be that way. Later, they lost the tidiness that we are now discussing.

However

1
   f x y [] 4

Would parse as an application, and here "[]" is the empty list.

I think this <i>is<i> problematic in that it can make it easy to commit whitespace errors. But in terms of customary appearance and its familiarity, I think that works. <quote> Finally, Jon Harrop has previously suggested that f -x -y -z would parse the "-" symbols as applications of unary negation, based again on a rule that says whitespace is significant.</quote> I favor that approach as well. There's always the problem of disambiguating unary versus binary "-" in an applicative-style notation and I think whitespace gets the nod, because it generally is how we have been trained to perceive these formulae. [I bet you knew a notation discussion would drag me out of the woodwork.]

By on 9/14/2007 9:52 AM ()

I have found the easiest way to disambiguate such nested calls has actually been to get LISPY. I use (f (g x)) instead of hoping f g x works. Admittedly, it's a bit funny when you start having to use tupled arguments to the .NET libraries, but if I start by resolving precedence issues by adding parentheses, the LISP approach has worked well for me. Then again, I find "mathematical" notation to be an ignoble goal, and don't mind a sea or parenthesis, so my aesthetic sensibilities may not mesh with the ML-origins of F#. I find the incredible variety of syntactic rules and exceptions to be very very daunting to a beginner (me!). Yes, once you have learned a few of the rules, they "mostly" "just work" until you get into the cases like are being discussed above, and they get very confusing very quickly.

By on 9/21/2007 1:30 PM ()

Hi Sebastian

Judging from the kind of code you posted to this forum I find it very hard to believe that you actually get confused by one or two simple syntax rules [;)]

In most cases the rules Don proposed above would "just work" for the beginner, especially someone with a .net (as oposed to a Lisp) background. Only the more curious user (especially the ML-influenced one) might then wonder why the code actually compiles and look up the syntax rules.

Furthermore, if you do not understand the syntax rules the compiler will typically help you by throwing a syntax error. It is hard to come up with a reasonable example where a misunderstanding would lead to code doing something unexpected.

This is also the argument I would hold against Dennis' objection. The proposed change of the precedence of f(x) vs f x would only make code compilable that previously was ambiguous or caused another syntax error, so there's little risk of an unpleasant surprise for existing users. I'm also in favor of the other two rules proposed by Don, though I'm not sure whether they could change the meaning of existing code for some contrived examples displaying bad taste for coding style (or whether previously compilable code might stop compiling). This is because I'm against bad taste and all for innovation [;)]

By on 9/22/2007 1:21 AM ()

This is also the argument I would hold against Dennis' objection. The proposed change of the precedence of f(x) vs f x would only make code compilable that previously was ambiguous or caused another syntax error, so there's little risk of an unpleasant surprise for existing users.

I was startled to see that I had made an objection, but I guess I did, although it was not about the proposal as such.

My concern here is not about syntax errors. I think it is deeper than that. It is that the forms

1
2
3
f x y z
f x y (z)
f x y(z)

Might all have more then one parsing and it will depend on type inference which parsing (in terms of the structure of applications) uh, applies, if any. We are in a situation where the presence and absence of incidental white space and ( ... ) are more than matters of syntax at least some of the time. I would be nervous about this, just as I am nervous about the off-side rules and knowing (1) switch/option settings and (2) which processor a code snippet author has in mind. These strike me as relevant global considerations.

This is a game that I have no skin in so I am not voting here, just kibbitzing. I have no perspective on how the Venn gerrymander of bad taste and innovation and innovative bad taste and tasteless innovation happens to carve up the territory (and who are the proper arbiters of taste and of innovation).

- Dennis

PS: Keep in mind that my personal applicative-notation preference for f x y z is that it always be absolutely equivalent to f(x(y(z))) where f x y z are free occurences in that fragment. I also favor f m[i, j] r(7) z working the same, where m[i,j] stands the same as x and r(7) as y in the f x y z form and (r 7) is no different than r(7), but [i, j] is not to be messed with. This is of course not a proposal for F# or any ML descendant and I am not competent to tune the F# grammar. The rules are purely syntactical though, and if the types don't work (if there are types), it's an error. But this is not F#.

By on 9/22/2007 9:04 AM ()

Just stumbled on this while catching up on RSS-feed backlog: Scott Bellware - To Parenthesize or Not To Parenthesize.

By on 9/22/2007 12:23 PM ()

I think that it would make sense to use whitespace to determine the order of applications (e.g. things that Don mentioned in his post). It would help a lot to a people with C# background, because they could just use the C#-like syntax when working with .NET methods and use the ML-like syntax when working with ML-like code in F#. I guess it could more tighty integrate these two different styles, so you wouldn't feel like using embedded C# in ML (or vice versa).

It is also a logical extension to the #light syntax - in a same way in which infering class/interface in object types is an extension to the F# type inference.

I'm just worried that it may be very difficult to explain the behavior properly to someone who want's t know how exactly it works (and I actually think that explaining #light syntax in this way isn't easy either). On the other side, I believe that it would "just work" in most of the cases (maybe after a few iterations?), so there wouldn't be need to explain it that often.

BTW: When speaking about F# syntax, are there any plans to modify the #light syntax rules a bit?
Here is one example where I find it too restrictive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
let someVarName = someObject.SomeMethod(someObject2.SomeMethod2(), someObject3.SomeMethod3())

// In #light syntax I usually end up with something like 
// this when I want to split the previous line:
let someVarName = 
  someObject.SomeMethod
    (someObject2.SomeMethod2(), 
     someObject3.SomeMethod3())

// But I would prefer writing something like this: (when I 
// just want to split a too long (but not complicated) line of code)
let someVarName = someObject.SomeMethod(someObject2.SomeMethod2(), 
  someObject3.SomeMethod3())
By on 9/22/2007 1:06 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper