Quantcast

Unifying Tuple types and function parameters

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Unifying Tuple types and function parameters

Andrew.Foggin
These thoughts are only half-formed, but I'm posting to the lounge to
help clarify my own thinking.  I'm sure that others have already
investigated this much more deeply and can point out all the holes!

A simple code example should explain the current situation:

  val f1 : (Int, Int) => Int = (a, b) => a + b
  val pair : (Int, Int) = (2, 2)
 
  f1(2, 2) // = 4
  // f1(pair)  // won't compile
 
  val f2 : ((Int, Int)) => Int = t => { val (a, b) = t; a + b }
 
  f2(2, 2) // = 4
  f2(pair) // = 4
 
  val f3 : (=> Int, => Int) => Int = { (a, b) => a + b }
 
  f3(2, 2) // = 4

  // won't compile
  //val f4 : ((=> Int, => Int)) => Int = t => { val (a, b) = t; a + b }
  //val pair2 : (=> Int, => Int) = (2, 2)
 
  val f5 : (Int*) => Int = seq => seq.reduceLeft[Int](_ + _)
 
  f5(2, 2) // = 4
 
  // Surprisingly this compiles
  val f6 : ((Int*)) => Int = seq => seq.reduceLeft[Int](_ + _)
 
  f6(2, 2) // = 4
 
  // but this doesn't of course
  // val pair3 : (Int*) = (2, 2)

So _if_ it was possible to add a more general CBN type '=> T' and allow
a repeated final element to a Tuple type '(... , T*)' then _perhaps_ a
function type could be more simply defined as A => B.

Any other thoughts?

Regards,

Andrew Foggin
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Henry Ware-2
I don't think the current situation is all that bad, but I'll play. :)

On Nov 14, 2007 11:21 PM, Andrew Foggin <[hidden email]> wrote:

>   // won't compile
>   //val f4 : ((=> Int, => Int)) => Int = t => { val (a, b) = t; a + b }
>   //val pair2 : (=> Int, => Int) = (2, 2)

This can get closer if the types are more explicit:

def f4b(x:Tuple2[Function0[Int],Function0[Int]]):Int={val (a,b)=x; a()*b()}
val f4c:((Tuple2[Function0[Int],Function0[Int]])=>Int) = x => {val
(a,b)=x; a()*b()}
val f4d: ((()=>Int,()=>Int))=>Int  = x => {val (a,b)=x; a()*b()}

def toTwo()=2
f4c(toTwo,toTwo) // =4
f4c((toTwo,toTwo)) //=4
val pair3:(()=>Int,()=>Int)=(toTwo,toTwo)
p4c(pair3) //=4

// f4c((2,2)) // found   : Int(2)  required: () => Int

// if we add this implicit, more things work
implicit def delay[T](t:T)=new Function0[T]{def apply()=t}

f4c(2,2) //=4
f4c((2,2)) //=4
val pair2 : (()=> Int,() => Int) = (2,2)
f4c(pair2) //=4

val pair =(2,2)
// f4c(pair) //  found   : (Int, Int),  required: (() => Int, () => Int)

So, if the function takes tuples, fixed length parameters work.
Unfortunately, requiring tuples is awkward, nonstandard and
inefficent---- unmashalling tuples into parameter lists is the more
interesting direction.

Implicit untupling doesn't work transparently:

val add:(Int,Int)=>Int = _+_;
add(2,3) //5

implicit def Untuple[A,B,C](f:Function2[A,B,C])=new Function1[Tuple2[A,B],C] {
def apply(x:Tuple2[A,B]):C={val (a:A,b:B)=x;f(a,b)}}
// type pattern is unchecked since it is eliminated by erasure

add apply ((2,3)) //5, conversion works here
// but not here:
// add ((2,3)) // wrong number of arguments

It's not to hard to wrap functions by hand:

case class F2[A,B,C](f:Function2[A,B,C]) extends Function2[A,B,C] with
Function1[Tuple2[A,B],C] {
def apply(a:A,b:B)=f(a,b);
def apply(t:Tuple2[A,B])={val (a:A,b:B)=t;f(a,b)}} // still unchecked

val add2=F2(add)
val mul2=F2[Int,Int,Int](_*_)

mul2((3,4))
mul2(6,7) //42

Other cases could be written for other Function lengths and an
overloaded method could construct them all;
I don't see how to extend that from functions to methods.

> So _if_ it was possible to add a more general CBN type '=> T' and allow
> a repeated final element to a Tuple type '(... , T*)' then _perhaps_ a
> function type could be more simply defined as A => B.

A function where all the elements are the same type is an unusually simple case.

-Henry
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Andrew.Foggin
Henry Ware wrote:
> I don't think the current situation is all that bad, but I'll play. :)
>
>  
I also don't think the current situation is that bad, I just think there
is scope for simplification.  Anyway, thanks for playing :)
>
> So, if the function takes tuples, fixed length parameters work.
> Unfortunately, requiring tuples is awkward, nonstandard and
> inefficent---- unmashalling tuples into parameter lists is the more
> interesting direction.
>
>  
Agreed!

>
> It's not to hard to wrap functions by hand:
>
> case class F2[A,B,C](f:Function2[A,B,C]) extends Function2[A,B,C] with
> Function1[Tuple2[A,B],C] {
> def apply(a:A,b:B)=f(a,b);
> def apply(t:Tuple2[A,B])={val (a:A,b:B)=t;f(a,b)}} // still unchecked
>
> val add2=F2(add)
> val mul2=F2[Int,Int,Int](_*_)
>
> mul2((3,4))
> mul2(6,7) //42
>
> Other cases could be written for other Function lengths and an
> overloaded method could construct them all;
> I don't see how to extend that from functions to methods.
>
>  
I'd expect methods to stay as they are.  But if tuples and function
parameters could be unified, there would be no more Function2, Function3
etc. - just Function1.  The compiler can translate a Function that takes
a Tuple to a method with multiple parameters.

>> So _if_ it was possible to add a more general CBN type '=> T' and allow
>> a repeated final element to a Tuple type '(... , T*)' then _perhaps_ a
>> function type could be more simply defined as A => B.
>>    
>
> A function where all the elements are the same type is an unusually simple case.
>
> -Henry
>
>  
Well that would be a Seq[T], not a Tuple.  Perhaps I could have
explained it better by saying that if it were possible to define a tuple
like this : (=> S, T, U*) then the parameter list for any function could
be replaced by a tuple.  As I said at the start, I'm sure there are
holes, I just don't see them yet!

-Andrew
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

John Nilsson

On Fri, 2007-11-16 at 15:17 +0900, Andrew Foggin wrote:
> > I don't see how to extend that from functions to methods.
> >
> >  
> I'd expect methods to stay as they are.  But if tuples and function
> parameters could be unified, there would be no more Function2, Function3
> etc. - just Function1.
What is the difference between function and methods, and why?

> Well that would be a Seq[T], not a Tuple.  Perhaps I could have
> explained it better by saying that if it were possible to define a tuple
> like this : (=> S, T, U*) then the parameter list for any function could
> be replaced by a tuple.  As I said at the start, I'm sure there are
> holes, I just don't see them yet!

I've been thinking that varargs generics would be nice many times. But
would it be useful for anything except tuples?

Regards,
John
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Andrew.Foggin
In reply to this post by Andrew.Foggin
OK.  Here's my proposal in more detail:

1. Generalise the '=> T' syntax sugar, so:

  val a : => Int = 1 + 2
  val b: Int = a

would desugar to:

  val a : () => Int = { () => 1 + 2 }
  val b: Int = a()

2. Extend the varargs syntax sugar to tuples, so:

  val c : (String, Int*) = ("abc", 1, 2, 3)

would desugar to:

  val c : (String, Seq[Int]) = ("abc", Array(1, 2, 3))

3. Equate Function{n}[A1, ... A{n}, R] to Function[Tuple{n}[A1, ...
A{n}], R]. The underlying implementation would stay the same, so:

  val f : (Int, Int) => Int = ...

still compiles to an anonymous class that in Java would look something like:

  class ... {
    ...
    public int apply(int arg0, int arg1) {
      ...
    }
  }

Does this make sense?

-Andrew


Andrew Foggin wrote:

> These thoughts are only half-formed, but I'm posting to the lounge to
> help clarify my own thinking.  I'm sure that others have already
> investigated this much more deeply and can point out all the holes!
>
> A simple code example should explain the current situation:
>
>  val f1 : (Int, Int) => Int = (a, b) => a + b
>  val pair : (Int, Int) = (2, 2)
>
>  f1(2, 2) // = 4
>  // f1(pair)  // won't compile
>
>  val f2 : ((Int, Int)) => Int = t => { val (a, b) = t; a + b }
>
>  f2(2, 2) // = 4
>  f2(pair) // = 4
>
>  val f3 : (=> Int, => Int) => Int = { (a, b) => a + b }
>
>  f3(2, 2) // = 4
>
>  // won't compile
>  //val f4 : ((=> Int, => Int)) => Int = t => { val (a, b) = t; a + b }
>  //val pair2 : (=> Int, => Int) = (2, 2)
>
>  val f5 : (Int*) => Int = seq => seq.reduceLeft[Int](_ + _)
>
>  f5(2, 2) // = 4
>
>  // Surprisingly this compiles
>  val f6 : ((Int*)) => Int = seq => seq.reduceLeft[Int](_ + _)
>
>  f6(2, 2) // = 4
>
>  // but this doesn't of course
>  // val pair3 : (Int*) = (2, 2)
>
> So _if_ it was possible to add a more general CBN type '=> T' and
> allow a repeated final element to a Tuple type '(... , T*)' then
> _perhaps_ a function type could be more simply defined as A => B.
>
> Any other thoughts?
>
> Regards,
>
> Andrew Foggin
>

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Andrew.Foggin
In reply to this post by John Nilsson
John Nilsson wrote:

> On Fri, 2007-11-16 at 15:17 +0900, Andrew Foggin wrote:
>  
>>> I don't see how to extend that from functions to methods.
>>>
>>>  
>>>      
>> I'd expect methods to stay as they are.  But if tuples and function
>> parameters could be unified, there would be no more Function2, Function3
>> etc. - just Function1.
>>    
> What is the difference between function and methods, and why?
>
>  
IIUC functions and methods are quite different in Scala.  This is
probably best explained in the Scala Language Specification where you'll
find function types described in section 3.2 (Value Types) and method
types described in section 3.3 (Non-Value Types).

Briefly, an unapplied or partially-applied method can be viewed as a
function value, while a function value can be thought of as an instance
of a class with an 'apply' method, which is how functions are
implemented behind-the-scenes.  But I think you know that, so I suspect
I'm not really answering the question you asked :-)

BTW I really like this article:
http://james-iry.blogspot.com/2007/08/kingdom-of-nerbs.html

>> Well that would be a Seq[T], not a Tuple.  Perhaps I could have
>> explained it better by saying that if it were possible to define a tuple
>> like this : (=> S, T, U*) then the parameter list for any function could
>> be replaced by a tuple.  As I said at the start, I'm sure there are
>> holes, I just don't see them yet!
>>    
>
> I've been thinking that varargs generics would be nice many times. But
> would it be useful for anything except tuples?
>
> Regards,
> John
>
>  
That would be enough for me.  I can't see how you would declare/use this
beyond tuples, but that's probably my lack of imagination!

-Andrew
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

John Nilsson

On Sun, 2007-11-18 at 13:20 +0900, Andrew Foggin wrote:
> IIUC functions and methods are quite different in Scala.  This is
> probably best explained in the Scala Language Specification where
> you'll
> find function types described in section 3.2 (Value Types) and method
> types described in section 3.3 (Non-Value Types).

The spec says:

"Method types do not exist as types of values. If a method name is used
as a value, its type is implicitly converted to a corresponding function
type (§6.25)."

So for all I can see the special status of methods has no consequences
on the programmer that the compiler couldn't hide.

Am I wrong?

Regards,
John
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Jamie Webb-2
On 2007-11-18 12:41:20 John Nilsson wrote:

>
> On Sun, 2007-11-18 at 13:20 +0900, Andrew Foggin wrote:
> > IIUC functions and methods are quite different in Scala.  This is
> > probably best explained in the Scala Language Specification where
> > you'll
> > find function types described in section 3.2 (Value Types) and
> > method types described in section 3.3 (Non-Value Types).
>
> The spec says:
>
> "Method types do not exist as types of values. If a method name is
> used as a value, its type is implicitly converted to a corresponding
> function type (§6.25)."
>
> So for all I can see the special status of methods has no consequences
> on the programmer that the compiler couldn't hide.
>
> Am I wrong?

Yes. A function (closure) is an object. A method is a member of a
class (i.e. is not a first-class value). One cannot define methods in
terms of functions not least because functions have methods. The most
notable of those is 'apply'.

Classes and methods are primitive language concepts. Functions are not.
They are just instances of the FunctionN classes in the standard
library. This also implies that functions cannot be polymorphic and
cannot be overloaded.

/J
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

John Nilsson

On Sun, 2007-11-18 at 12:03 +0000, Jamie Webb wrote:

> On 2007-11-18 12:41:20 John Nilsson wrote:
> >
> > On Sun, 2007-11-18 at 13:20 +0900, Andrew Foggin wrote:
> > > IIUC functions and methods are quite different in Scala.  This is
> > > probably best explained in the Scala Language Specification where
> > > you'll
> > > find function types described in section 3.2 (Value Types) and
> > > method types described in section 3.3 (Non-Value Types).
> >
> > The spec says:
> >
> > "Method types do not exist as types of values. If a method name is
> > used as a value, its type is implicitly converted to a corresponding
> > function type (§6.25)."
> >
> > So for all I can see the special status of methods has no consequences
> > on the programmer that the compiler couldn't hide.
> >
> > Am I wrong?
>
> Yes. A function (closure) is an object. A method is a member of a
> class (i.e. is not a first-class value). One cannot define methods in
> terms of functions not least because functions have methods. The most
> notable of those is 'apply'.
>
> Classes and methods are primitive language concepts. Functions are not.
> They are just instances of the FunctionN classes in the standard
> library. This also implies that functions cannot be polymorphic and
> cannot be overloaded.
>
> /J

Thanks you for that. I can see how that can be problematic given the
current design.

I could imagine an approach where apply() was implemented as some other
type though. Something more primitive than function, Seq[Stmt] or other
representation of executable construct f.ex. But that is an entirely
different language :P

Regards,
John
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Unifying Tuple types and function parameters

Adriaan Moors-2
In reply to this post by Andrew.Foggin

On 18 Nov 2007, at 05:20, Andrew Foggin wrote:

>> I've been thinking that varargs generics would be nice many times.  
>> But
>> would it be useful for anything except tuples?
>>
>> Regards,
>> John
>>
>>
> That would be enough for me.  I can't see how you would declare/use  
> this beyond tuples, but that's probably my lack of imagination!
Indeed ;-), have a look at what (among others) Tim Sheard is doing in  
Omega (http://web.cecs.pdx.edu/~sheard/papers/SumSchNotes.ps)

I posted about this a while ago: http://article.gmane.org/ 
gmane.comp.lang.scala.user/595  (generalising tuples to records)

In the meantime, I've been experimenting with tuples as well:
http://kinded-scala.googlecode.com/svn/trunk/examples/src/scala/ 
examples/tcpoly/typecomp/tuples/tuples.scala

I hope to write a short article about this by year's end.

adriaan
Loading...