On Thu, Feb 12, 2015 at 11:34 AM, Keean Schupke <[email protected]> wrote:

> On 12 February 2015 at 18:32, Jonathan S. Shapiro <[email protected]>
> wrote:
>
>>
>> One effect of this in F# is that *programmers* seem to get divided into
>> two camps. One camp comes from the functional programming world. That camp
>> is comfortable with the curried application syntax, and will use it
>> preferentially. The other camp comes from the world of C. That camp will
>> also gravitate toward what is familiar, which is to say the tuple syntax.
>> The two groups will tend to build libraries in mutually incompatible style,
>> which raises a concern that the user base will ultimately bifurcate.
>>
>
> I see the problem. Tuples exist in most functional languages and I think
> they are useful. I don't think I would want to get rid of them, so this
> always leaves the possibility of creating different function signatures.
> Any arity system would add a third syntax on top of curried and tuples,
> which might just lead to further confusion and different implementations.
>

I think we are talking past each other again, but this time only in a small
way. In brief, I don't think that any of this messes with tuple use in the
slightest, and we don't need a third syntax.

The rules for arity analysis in F# are [under]specified by example in
section 14.10 the F# specification. They are different for functions and
methods, and I am now realizing that they don't rely on tuples as heavily
as I had imagined. The issue at hand, from the F# perspective, isn't so
much the internal calling convention as it is the need to report a CLI type
for each exported function and method. The rules are subtly different for
functions and methods, in a way that I don't yet fully understand.

Here's a general sketch of how this works for the *simple* cases (those not
involving first-class functions). Given

   def f a b c ... z // formal parameters may be patterns

1. All decisions about "implementation level arity" are made based on on
the *concrete* function type, after any type variables have been resolved
to concrete types.

2. A function taking a single argument of type unit is implemented as an
arity-0 function.

3. For all other functions, the argument patterns a..z are examined in
turn, and an arity is assigned to each formal parameter pattern. If the
pattern is NOT a tuple, it is assigned an arity of 1. If the pattern is a
typle pattern, then it's arity is the length of the tuple. The final arity
of the function in the eyes of CLI is the sum of the parameter arities. So
from the CLI perspective, all of the following functions have arity 3:

  def f a b c = ...
  def f a (b, c) = ...
  def f (a, b, c) = ...

So far so good. I think we could tighten up the specification above to make
it rigorous, and that would give us a fine way to define a notion of
"native arity". Note that none of the above changes how F# handles
application internally, but note also that an application of the form "f
(arg1, arg2)" is pretty well guaranteed to produce the behavior we want for
systems codes. It may be the case for systems codes that we should simply
use tuple-style arguments in all cases.

There is also discussion in the F# specification that the following is
inferred to be an arity-2 function at the CLI level:

  def f x = fun y -> ...body...

They don't specify the inference rules anywhere, so I'm not sure exactly
which cases are inferred in this way.


Now all of this is, in a certain sense, answering the *reverse* of the
question we have been asking. It is asking how F# goes about constructing
the "reverse" wrapper for use by less enlightened languages. Within the
language, if the function being called can be statically resolved, it is
certainly possible to call the native-arity version of the function.


But when the function *cannot* be statically resolved, there is a problem.
Given a function parameter f with type 'a->'b->'c, and an application of
that function occurring in tail position, I do not see how we can determine
whether the application

  f a

is an allocating application. This creates a very strange conundrum for
effect inference, because there are cases where

  f a

will allocate (because it is partial) while

  f a b

will not. The overall allocation effect cannot be deduced by composing the
allocation effects on the individual arrows.



> One purpose of the present discussion about arity-aware function types was
>> to see if we could satisfy the needs of both groups with a single surface
>> syntax.
>>
>
> Would you get rid of tuples then? I see what you are trying to do, but it
> seems problematic.
>

No, no. I wasn't thinking about getting rid of tuples. In fact, BitC tuples
are syntactic sugar on top of pairs at the moment, and I think we either
need to fix that or add a Nat to the type so that we can keep track of the
length.


> What about named arguments (just pass a record).
>

Indeed. We're going to need to support optional arguments at some point as
well. I don't know how to do those cleanly in the curried application style.


shap
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to