Ada 202x proposal # restriction rationale? - ada

I have a question about the proposed Ada 202x standard. Specifically I am wondering about the constraints on the target name (#) feature. It appears as though this feature will only be available in assignment statements and not in record or array aggregates. What is the rationale behind this restriction?
To explain why I think it would be useful to broaden the scope of target name. Compare these two functions.
Implementation as I understand the proposed standard:
function Mutate (State : Some_State) return Some_State is (State with delta
Internal_Component => (State.Internal_Component with delta
Component_Array => (State.Internal_Component.Component_Array with delta
Array_Index => State.Internal_Component.Component_Array (Array_Index) + 1,
Other_Index => State.Internal_Component.Component_Array (Other_Index) - 1)));
If the target name feature were extended to aggregates then this could be:
function Mutate (State : Some_State) return Some_State is (State with delta
Internal_Component => (# with delta -- # is State.Internal_Component
Component_Array => (# with delta -- # is State.Internal_Component.Component_Array
Array_Index => # + 1, -- # is State.Internal_Component.Component_Array (Array_Index)
Other_Index => # - 1))); -- # is State.Internal_Component.Component_Array (Other_Index)
If delta aggregates could implicitly nest this could result in the very clean:
function Clean_Mutate (State : Some_State) return Some_State is (State with delta
Internal_Component.Component_Array => # with delta (
Array_Index => # + 1,
Other_Index => # - 1));
Of course as written one could use:
function Mutate (State : Some_State) return Some_State is
Result : Some_State := State;
begin
Result.Internal_Component.Component_Array (Array_Index) := # + 1;
Result.Internal_Component.Component_Array (Other_Index) := # - 1;
return Result;
end Mutate;
But this is still not quite as clean, and it severely hampers target name's use in non-assignment expressions.

Related

converting to tail recursion

Consider a function g(x):
g(x) = x == 0 ? 0 : g(g(x - 1))
I don't think this is tail recursive, since the call to g(x) can be broken down into two parts:
let y = g(x - 1);
return g(y);
how to convert this to tail recursion?
continuation-passing style
You can convert from direct style to continuation-passing style -
g'(x,return) =
x == 0
? return(0)
: g'(x - 1, x' => # tail
g'(x', return)) # tail
Now write g to call g' with the default continuation -
g(x) = g'(x, x' => x')
Depending on the language you use, you may have to rename return to something else. k is a popular choice.
another example
We can see this technique applied to other problems like fib -
# direct style
fib(x) =
x < 2
? x
: fib(x - 1) + fib(x - 2) # "+" has tail position; not fib
# continuation-passing style
fib'(x, return) =
x < 2
? return(x)
: fib'(x - 1, x' => # tail| first compute x - 1
fib(x - 2, x'' => # tail| then compute x - 2
return(x' + x''))) # tail| then return sum
fib(x) = fib'(x, z => z)
other uses
Continuation-passing style is useful in other ways too. For example, it can be used to provide branching or early-exit behavior in this search program -
search(t, index, match, ifFound, notFound) =
i >= length(t)
? notFound()
: t[index] == match
? ifFound(index)
: search(t, index + 1, match, ifFound, notFound)
We can call search with continuations for each possible outcome -
search(["bird", "cat", "dog"],
0,
"cat",
matchedIndex => print("found at: " + matchedIndex),
() => print("not found")
)
how to
A function written in continuation-passing style takes an extra argument: an explicit "continuation"; i.e., a function of one argument — wikipedia
(* direct style *)
add(a, b) = ...
(* continuation-passing style takes extra argument *)
add(a, b, k) = ...
When the CPS function has computed its result value, it "returns" it by calling the continuation function with this value as the argument.
(* direct style *)
add(a, b) =
a + b
(* continuation-passing style "returns" by calling continuation *)
add(a, b, k) =
k(a + b) (* call k with the return value *)
That means that when invoking a CPS function, the calling function is required to supply a procedure to be invoked with the subroutine's "return" value.
(* direct style *)
print(add(5, 3)) (* 8 *)
(* continuation-passing style *)
add(5, 3, print) (* 8 *)
Expressing code in this form makes a number of things explicit which are implicit in direct style. These include: procedure returns, which become apparent as calls to a continuation; intermediate values, which are all given names; order of argument evaluation, which is made explicit; and tail calls, which simply call a procedure with the same continuation, unmodified, that was passed to the caller.
you've probably used continuations before
If you've ever run into someone saying "callback", what they really mean is continuation.
"When the button is clicked, continue the program with event => ..." -
document.querySelector("#foo").addEventListener("click", event => {
console.log("this code runs inside a continuation!")
})
<button id="foo">click me</button>
"When the file contents are read, continue the program with (err, data) => ..." -
import { readFile } from 'fs';
readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});

Point-free pipe range with Ramdajs

I have my function that creates ranges:
const range = from => to => step => ....
And i want to create another function that do something with that range, but i want to use pipe.
For example i want to get the sum of the range.
const sum = ...
const getSum = pipe(
range(),
sum,
)
I can to the following:
const getSum = from => to => {
return sum(range(from)(to)(1))
}
But can I do it with ramdajs and to be point free.
For example:
const getSum = pipe(
range..///here,
sum
)
pipe, sum and range are my implementations.
But i am not sure how to do it point-free style.
Note that range is function that return function for easier currying.
range(0)(10)(1)
Thank you
More description:
Imagine i have count and split functions.
This is regular function:
const numChars = str => count(split('', str))
This is point-free style (imag
const numChars = pipe(split(''), count)
I want to the the same but with the range above
To compose with a function taking more than one argument, use composition multiple times - although you need to use R.o instead of pipe or compose:
// sum: [Number] -> Number
// range: Number -> Number -> [Number]
// getSum: Number -> Number -> Number
const getSum = R.o(R.o(R.sum), R.range);
To supply the last argument of a curried function, the easiest Ramda solution is using a placeholder:
// stepRange: Number -> Number -> Number -> [Number]
// range: Number -> Number -> [Number]
const range = stepRange(R.__, R.__, 1);
but this only works with functions created by Ramda's own curryN or curry, not with manually curried functions. With those, you can only R.flip until you can partially apply the first argument:
// stepRange: from -> to -> step -> range
// flip(stepRange): to -> from -> step -> range
// o(flip, stepRange): from -> step -> to -> range
// flip(o(flip, stepRange)): step -> from -> to -> range
// range: from -> to -> range
const range = R.flip(R.o(R.flip, stepRange))(1);
I cannot really recommend doing this. Everything is possible, but this is unreadable. Just write out the parameters:
const getSum = R.o(R.o(R.sum), R.flip(R.o(R.flip, stepRange))(1));
// vs
const getSum = from => to => R.sum(stepRange(from)(to)(1));

How to functionally convert a nested hash to a list of records?

Let's say I have a nested hash describing money quantities:
my %money = (coins => {'50c' => 4}, notes => {'10' => 1, '20' => 5});
My desired format is a record list:
my #money = [
(:type('coins'), :subtype('50c'), value => 4),
(:type('notes'), :subtype('10'), value => 1),
(:type('notes'), :subtype('20'), value => 5),
];
The most obvious answer is loops:
my #money;
for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
#money.push: (:$type, :$subtype, :$value);
}
}
But I'm allergic to separating a variable from the code that populates it. Next, I tried to create the variable with functional transformations on the input hash:
%money.kv.map: -> $k1, %hsh2 { :type($k1) X, %hsh2.kv.map(->$k2, $v2 {:subtype($k2), :$v2, :value($v2)}) }
But I didn't get the nesting right. I want a list of flat lists. Plus, the above is a mess to read.
The compromise is the gather/take construct which lets me construct a list by iteration without any temporary/uninitialized junk in the main scope:
my #money = gather for %money.kv -> $type, %sub-records {
for %sub-records.kv -> $subtype, $value {
take (:$type, :$subtype, :$value);
}
};
But I'm curious, what is the right way to get this right with just list transformations like map, X or Z, and flat? ("key1", "key2", and "value" are fine field names, since an algorithm shouldn't be domain specific.)
Edit: I should mention that in Perl 6, gather/take is the most readable solution (best for code that's not write-only). I'm still curious about the pure functional solution.
my #money = %money.map:
-> ( :key($type), :value(%records) ) {
slip
:$type xx *
Z
( 'subtype' X=> %records.keys )
Z
( 'value' X=> %records.values )
}
You could do .kv.map: -> $type, %records {…}
-> ( :key($type), :value(%records) ) {…} destructures a Pair object
:$type creates a type => $type Pair
:$type xx * repeats :$type infinitely (Z stops when any of it's inputs stops)
('subtype' X=> %records.keys) creates a list of Pairs
(Note that .keys and .values are in the same order if you don't modify the Hash between the calls)
Z zips two lists
slip causes the elements of the sequence to slip into the outer sequence
(flat would flatten too much)
If you wanted them to be sorted
my #money = %money.sort.map: # 'coins' sorts before 'notes'
-> ( :key($type), :value(%records) ) {
# sort by the numeric part of the key
my #sorted = %records.sort( +*.key.match(/^\d+/) );
slip
:$type xx *
Z
( 'subtype' X=> #sorted».key )
Z
( 'value' X=> #sorted».value )
}
You could do .sort».kv.map: -> ($type, %records) {…}

How to stop recursing?

Advent of Code Day 1 requires looping, in one form or another, over a long string of parentheses like ((((())(())(((()))(( etc. The idea is that ( goes up one "floor", ) goes down one floor, and the objectives are to print
the first index in the string where the floor number is negative and
the final floor when the end of the string is found.
The imperative solution with a for loop is simple (Python as an example):
def main():
flr = 0
basement = False
for idx, elt in enumerate(text):
flr += {
"(": 1,
")": -1
}.get(elt)
if flr < 0 and not basement:
print("first basement pos:", idx + 1)
basement = True
print("final floor:", flr)
The recursive functional solution is a little more complex, but still not too hard.
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return flr
if flr < 0 and not basement:
print("first basement floor index: ", idx + 1)
basement = True
return worker(flr, txt[1:], idx + 1, basement)
def starter(txt):
flr, basement, idx = 0, False, 0
return worker(flr, txt, idx, basement)
if __name__ == '__main__':
__import__("sys").setrecursionlimit(int(1e5))
print("final floor:", starter(text))
Both of these give the correct output of
first basement floor index: 1795
final floor: 74
when run against my challenge input.
except the second one is dumb because Python doesn't have tail call optimisation but never mind that
How can I implement either of these in Factor? This is something I've been confused by ever since I started using Factor.
We can't just use a for loop because there's no equivalent that allows us to keep mutable state between iterations.
We could use a recursive solution:
: day-1-starter ( string -- final-floor )
[ 0 ] dip 0 f day-1-worker 3drop "final floor: %s" printf ;
: day-1-worker
( floor string index basement? -- floor string index basement? )
day-1-worker ! what goes here?
; recursive
Great, that's a skeleton, but what goes in the body of day-1-worker? Factor doesn't have any way to "early return" from a recursive call because there's no way to run the program in reverse and no concept of return -- that doesn't make any sense.
I get the feeling maybe recursion isn't the answer to this question in Factor. If it is, how do I stop recursing?
First of all, recursion is always the answer :)
Since this is a challenge (and I don't know factor), just a hint:
in your python solution you have used the side effect to print the first basement level. Quite unnecessary! You can use basemet argument to hold the floor number too, like this:
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return [flr, basement] # <- return both
if flr < 0 and not basement:
#print("first basement floor index: ", idx + 1) # side effects go away!
basement = idx+1 # <- a number in not False, so that's all
return worker(flr, txt[1:], idx + 1, basement)
So now you get
final,first_basement = worker(0, txt, 0, False)
Or, alternatively you can write 2 functions, first one seeks the index of first basement floor, the other one just computes the final floor. Having <2000 additional small steps is not a big deal even if you do care about performance.
Good luck!
Edit: as of your question concerning recursion in factor, take a look at the Ackermann Function in Factor and the Fibonacci sequence in Factor and you should get the idea how to "break the loop". Actually the only problem is in thinking (emancipate yourself from the imperative model :)); in functional languages there is no "return", just the final value, and stack-based languages you mention are other computational model of the same thing (instead of thinking of folding a tree one thinks about "pushing and poping to/from the stacks" -- which is btw a common way to implement the former).
Edit: (SPOILER!)
I installed Factor and started playing with it (quite nice), for the first question (computing the final score) a possible solution is
: day-1-worker ( string floor -- floor )
dup length 0 =
[ drop ]
[ dup first 40 =
[ swap 1 + ]
[ swap 1 - ]
if
swap rest
day-1-worker ]
if ;
: day-1-starter ( string -- floor )
0 swap day-1-worker ;
So now you can either write similar one for computing basement's index, or (which would be more cool!) to modify it so that it also manages index and basement... (Probably using cond would be wiser than nesting ifs).
You could use the cum-sum combinator:
: to-ups/downs ( str -- seq )
[ CHAR: ( = 1 -1 ? ] { } map-as ;
: run-elevator ( str -- first-basement final-floor )
to-ups/downs cum-sum [ -1 swap index 1 + ] [ last ] bi ;
IN: scratchpad "((())))(())(())(((()))((" run-elevator
--- Data stack:
7
2
EDIT
I originally misread how your were computing the basement value. I've updated the answers below
Here's a JavaScript solution. Sorry I have no idea how this converts to Factor. reduce is an iterative process
const worker = txt=>
txt.split('').reduce(({floor, basement}, x, i)=> {
if (x === '(')
return {floor: floor + 1, basement}
else if (basement === null && floor === 0)
return {floor: floor - 1, basement: i}
else
return {floor: floor - 1, basement}
}, {floor: 0, basement: null})
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
The answer above relies on some some .split and .reduce which may not be present in your language. Here's another solution using Y-combinator and only the substring built-in (which most languages include). This answer also depends on your language having first-class functions.
const U = f=> f (f)
const Y = U (h=> f=> f (x=> h (h) (f) (x)))
const strhead = s=> s.substring(0,1)
const strtail = s=> s.substring(1)
const worker = Y (f=> ({floor, basement})=> i=> txt=> {
// txt is empty string; return answer
if (txt === '')
return {floor, basement}
// first char in txt is '(', increment the floor
else if (strhead (txt) === '(')
return f ({floor: floor + 1, basement}) (i+1) (strtail (txt))
// if basement isn't set and we're on floor 0, we found the basement
else if (basement === null && floor === 0)
return f ({floor: floor - 1, basement: i}) (i+1) (strtail (txt))
// we're already in the basement, go down another floor
else
return f ({floor: floor - 1, basement}) (i+1) (strtail (txt))
}) ({floor: 0, basement: null}) (0)
{
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
}
{
let {floor, basement} = worker(')(((((')
console.log(floor) //=> 4
console.log(basement) //=> 0
}
{
let {floor, basement} = worker('((())))')
console.log(floor) //=> -1
console.log(basement) //=> 6
}

What does the lambda calculus have to say about return values?

It is by now a well known theorem of the lambda calculus that any function taking two or more arguments can be written through currying as a chain of functions taking one argument:
# Pseudo-code for currying
f(x,y) -> f_curried(x)(y)
This has proven to be extremely powerful not just in studying the behavior of functions but in practical use (Haskell, etc.).
Functions returning values, however, seem to not be discussed. Programmers typically deal with their inability to return more than one value from a function by returning some meta-object (lists in R, structures in C++, etc.). It has always struck me as a bit of a kludge, but a useful one.
For instance:
# R code for "faking" multiple return values
uselessFunc <- function(dat) {
model1 <- lm( y ~ x , data=dat )
return( list( coef=coef(model1), form=formula(model1) ) )
}
Questions
Does the lambda calculus have anything to say about a multiplicity of return values? If so, do any surprising conclusions result?
Similarly, do any languages allow true multiple return values?
According to the Wikipedia page on lambda calculus:
Lambda calculus, also written as λ-calculus, is a formal system for function
definition, function application and recursion
And a function, in the mathematical sense:
Associates one quantity, the argument of the function, also known as the input,
with another quantity, the value of the function, also known as the output
So answering your first question no, lambda calculus (or any other formalism based on mathematical functions) can not have multiple return values.
For your second question, as far as I know, programming languages that implement multiple return values do so by packing multiple results in some kind of data structure (be it a tuple, an array, or even the stack) and then unpacking it later - and that's where the differences lie, as some programming languages make the packing/unpacking part transparent for the programmer (for instance Python uses tuples under the hood) while other languages make the programmer do the job explicitly, for example Java programmers can simulate multiple return values to some extent by packing multiple results in a returned Object array and then extracting and casting the returned result by hand.
A function returns a single value. This is how functions are defined in mathematics. You can return multiple values by packing them into one compound value. But then it is still a single value. I'd call it a vector, because it has components. There are vector functions in mathematics there, so there are also in programming languages. The only difference is the support level from the language itself and does it facilitate it or not.
Nothing prevents you from having multiple functions, each one returning one of the multiple results that you would like to return.
For example, say, you had the following function in python returning a list.
def f(x):
L = []
for i in range(x):
L.append(x * i)
return L
It returns [0, 3, 6] for x=3 and [0, 5, 10, 15, 20] for x=5. Instead, you can totally have
def f_nth_value(x, n):
L = []
for i in range(x):
L.append(x * i)
if n < len(L):
return L[n]
return None
Then you can request any of the outputs for a given input, and get it, or get None, if there aren't enough outputs:
In [11]: f_nth_value(3, 0)
Out[11]: 0
In [12]: f_nth_value(3, 1)
Out[12]: 3
In [13]: f_nth_value(3, 2)
Out[13]: 6
In [14]: f_nth_value(3, 3)
In [15]: f_nth_value(5, 2)
Out[15]: 10
In [16]: f_nth_value(5, 5)
Computational resources may be wasted if you have to do some of the same work, as in this case. Theoretically, it can be avoided by returning another function that holds all the results inside itself.
def f_return_function(x):
L = []
for i in range(x):
L.append(x * i)
holder = lambda n: L[n] if n < len(L) else None
return holder
So now we have
In [26]: result = f_return_function(5)
In [27]: result(3)
Out[27]: 15
In [28]: result(4)
Out[28]: 20
In [29]: result(5)
Traditional untyped lambda calculus is perfectly capable of expressing this idea. (After all, it is Turing complete.) Whenever you want to return a bunch of values, just return a function that can give the n-th value for any n.
In regard to the second question, python allows for such a syntax, if you know exactly, just how many values the function is going to return.
def f(x):
L = []
for i in range(x):
L.append(x * i)
return L
In [39]: a, b, c = f(3)
In [40]: a
Out[40]: 0
In [41]: b
Out[41]: 3
In [42]: c
Out[42]: 6
In [43]: a, b, c = f(2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-43-5480fa44be36> in <module>()
----> 1 a, b, c = f(2)
ValueError: need more than 2 values to unpack
In [44]: a, b, c = f(4)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-44-d2c7a6593838> in <module>()
----> 1 a, b, c = f(4)
ValueError: too many values to unpack
Lastly, here is an example from this Lisp tutorial:
;; in this function, the return result of (+ x x) is not assigned so it is essentially
;; lost; the function body moves on to the next form, (* x x), which is the last form
;; of this function body. So the function call only returns (* 10 10) => 100
* ((lambda (x) (+ x x) (* x x)) 10)
=> 100
;; in this function, we capture the return values of both (+ x x) and (* x x), as the
;; lexical variables SUM and PRODUCT; using VALUES, we can return multiple values from
;; a form instead of just one
* ((lambda (x) (let ((sum (+ x x)) (product (* x x))) (values sum product))) 10)
=> 20 100
I write this as a late response to the accepted answer since it is wrong!
Lambda Calculus does have multiple return values, but it takes a bit to understand what returning multiple values mean.
Lambda Calculus has no inherent definition of a collection of stuff, but it does allow you to invent it using products and church numerals.
pure functional JavaScript will be used for this example.
let's define a product as follows:
const product = a => b => callback => callback(a)(b);
then we can define church_0, and church_1 aka true, false, aka left, right, aka car, cdr, aka first, rest as follows:
const church_0 = a => b => a;
const church_1 = a => b => b;
let's start with making a function that returns two values, 20, and "Hello".
const product = a => b => callback => callback(a)(b);
const church_0 = a => b => a;
const church_1 = a => b => b;
const returns_many = () => product(20)("Hello");
const at_index_zero = returns_many()(church_0);
const at_index_one = returns_many()(church_1);
console.log(at_index_zero);
console.log(at_index_one);
As expected, we got 20 and "Hello".
To return more than 2 values, it gets a bit tricky:
const product = a => b => callback => callback(a)(b);
const church_0 = a => b => a;
const church_1 = a => b => b;
const returns_many = () => product(20)(
product("Hello")(
product("Yes")("No")
)
);
const at_index_zero = returns_many()(church_0);
const at_index_one = returns_many()(church_1)(church_0);
const at_index_two = returns_many()(church_1)(church_1)(church_0);
console.log(at_index_zero);
console.log(at_index_one);
console.log(at_index_two);
As you can see, a function can return an arbitrary number of return values, but to access these values, a you cannot simply use result()[0], result()[1], or result()[2], but you must use functions that filter out the position you want.
This is mindblowingly similar to electrical circuits, in that circuits have no "0", "1", "2", "3", but they do have means to make decisions, and by abstracting away our circuitry with byte(reverse list of 8 inputs), word(reverse list of 16 inputs), in this language, 0 as a byte would be [0, 0, 0, 0, 0, 0, 0, 0] which is equivalent to:
const Byte = a => b => c => d => e => f => g => h => callback =>
callback(a)(b)(c)(d)(e)(f)(g)(h);
const Byte_one = Byte(0)(0)(0)(0)(0)(0)(0)(1); // preserves
const Bit_zero = Byte_one(b7 => b6 => b5 => b4 => b3 => b2 => b1 => b0 => b0);
After inventing a number, we can make an algorithm to, given a byte-indexed array, and a byte representing index we want from this array, it will take care of the boilerplate.
Anyway, what we call arrays is nothing more than the following, expressed in higher level to show the point:
// represent nested list of bits(addresses)
// to nested list of bits(bytes) interpreted as strings.
const MyArray = function(index) {
return (index == 0)
? "0th"
: (index == 1)
? "first"
: "second"
;
};
except it doesnt do 2^32 - 1 if statements, it only does 8 and recursively narrows down the specific element you want. Essentially it acts exactly like a multiplexor(except the "single" signal is actually a fixed number of bits(coproducts, choices) needed to uniquely address elements).
My point is that is Arrays, Maps, Associative Arrays, Lists, Bits, Bytes, Words, are all fundamentally functions, both at circuit level(where we can represent complex universes with nothing but wires and switches), and mathematical level(where everything is ultimately products(sequences, difficult to manage without requiring nesting, eg lists), coproducts(types, sets), and exponentials(free functors(lambdas), forgetful functors)).

Resources