2

Is it possible, that a function can return a function in Ada? I am trying to get currying to work.

type Integer_Func_Type is access function (Y : Integer) return Integer;

function Add (X : Integer) return Integer_Func_Type is
   function Inner (Y : Integer) return Integer is
   begin
      return X + Y;
   end Inner;
begin
   return Inner'Access;
end;

At the end, I do not want to provide all arguments of a function one at a time. For example: if x is a ternary function and y is curry(x), then I can use following function calls: y(a,b,c), y(a,b)(c), y(a)(b,c), y(a)(b)(c).

EDIT

I implemented 'Jacob Sparre Andersen' suggestions. But it does not look like currying will be easy to implement. I must implement every possible variant of any type I want to use in advance. Is this correct?

with Ada.Text_IO;

with R;

procedure Hello is
   Add_Two : R.Test2 := (X => 2);
begin
   Ada.Text_IO.Put_Line(Add_Two.Add(3)'Img);
end Hello;

r.adb

package body R is

   function Add(A : Test2; Y : Integer) return Integer is
   begin
      return A.X + Y;
   end Add;

end R;

r.ads

package R is

   type Test is abstract tagged null record;

   function Add(A : Test; Y : Integer) return Integer is abstract;

   type Test2 is new Test with
      record
         X : Integer;
      end record;

   overriding
   function Add(A : Test2; Y : Integer) return Integer;

end R;
user1091344
  • 612
  • 6
  • 27
  • I think you'll have problems with the access rules/scoping for the inner function. – Dale Stanbrough Aug 22 '17 at 01:08
  • @DaleStanbrough I see. Do you think, currying is even possible? – user1091344 Aug 22 '17 at 17:30
  • Ada doesn't do type inference, so yes, you have to declare each kind of function (parameters and return type) you need as a separate tagged type. – Jacob Sparre Andersen Aug 23 '17 at 18:35
  • Chris Okasaki has addressed this in [Functional Programming in ...Ada?](http://okasaki.blogspot.de/2008/07/functional-programming-inada.html). – B98 Sep 11 '17 at 18:51
  • No, in short, you cannot really tie a function to an environment (LISP sense) that exists only locally, so **return**ing a function and this kind of environment is not possible in Ada. However, some, if not most, compilers will let you take risks by providing the non-Ada attribute `'Unrestricted_Access`. So, if you can prolong the lifetime of objects to which this function (pointer) is referring beyond what is required by the language... – B98 Sep 11 '17 at 18:55

3 Answers3

3

This is how to do it with generics:

with Ada.Text_IO;

procedure Test is
   --  shorthand Ada 2012 syntax; can also use full body
   function Add (X, Y : Integer) return Integer is (X + Y);

   generic
      type A_Type (<>) is limited private;
      type B_Type (<>) is limited private;
      type Return_Type (<>) is limited private;
      with function Orig (A : A_Type; B : B_Type) return Return_Type;
      A : A_Type;
   function Curry_2_to_1 (B : B_Type) return Return_Type;

   function Curry_2_to_1 (B : B_Type) return Return_Type is
     (Orig (A, B));

   function Curried_Add is new Curry_2_to_1
     (Integer, Integer, Integer, Add, 3);
begin
   Ada.Text_IO.Put_Line (Integer'Image (Curried_Add (39)));
end Test;

As you see, it is quite verbose. Also, you need to provide a currying implementation for every count X of parameters of the original function and every number Y of parameters of the generated function, so you'd have a lot of Curry_X_to_Y functions. This is necessary because Ada does not have variadic generics.

A lot of the verbosity also comes from Ada not doing type inference: You need to explicitly specifiy A_Type, B_Type and Return_Type even though theoretically, they could be inferred from the given original function (this is what some functional programming languages do).

Finally, you need a named instance from the currying function because Ada does not support anonymous instances of generic functions.

So, in principle, currying does work, but it is not anything as elegant as in a language like Haskell. If you only want currying for a specific type, the code gets significantly shorter, but you also lose flexibility.

flyx
  • 35,506
  • 7
  • 89
  • 126
1

You can't do quite what you're trying to do, since Inner stops to exist as soon as Add returns.

You could do something with the effect you describe using tagged types.

One abstract tagged type with a primitive operation matching your function type.

And then a derived tagged type with X as an attribute and an implementation of the function matching Inner.

Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
1

Many of the answers seem to deal with ways to have subprograms that deal with variable numbers of parameters. One way to deal with this is with a sequence of values. For example,

type Integer_List is array (Positive range <>) of Integer;
function Add (List : Integer_List) return Integer;

can be considered a function that takes an arbitrary number of parameters of type Integer. This is simple if all your parameters have the same type. It's more complicated, but still possible, if you deal with a finite set of possible parameter types:

type Number_ID is (Int, Flt, Dur);
type Number (ID : Number_ID) is record
   case ID is
   when Int =>
      Int_Value : Integer;
   when Flt =>
      Flt_Value : Float;
   when Dur =>
      Dur_Value : Duration;
   end case;
end record;
type Number_List is array (Positive range <>) of Number;
function Add (List : Number_List) return Number;

If you have to be able to handle types not known in advance, this technique is not suitable.

Jeffrey R. Carter
  • 3,033
  • 9
  • 10
  • A related example using an _array aggregate_ is examined [here](https://stackoverflow.com/a/33093352/230513). – trashgod Aug 24 '17 at 23:51