Challenge: Add an “Array-like notation” to the Calculator
Objective
Add an array notation to the language with a syntax like the illustrated in the example below:
a = [4+2;5+3i;9-i], # Creates a new function 0 => 4+2, 1 => 5+3i, 2 => 9-i
print(a(0)), # { re: 6, im: 0 }
a(1) = 333,
print(a(1)), # { re: 333, im: 0 }
print(a) # { re: 6, im: 0 }, { re: 333, im: 0 }, { re: 9, im: -1 } ] or s.t. similar
Where the assignment a = [4+2;5+3i;9-i]
creates a new function
a
that maps indexes to values: 0 => 4+2
, 1 => 5+3i
, 2 => 9-i
.
The execution of the code above should print an output like the following:
âžś left-side-solution git:(debug) âś— bin/calc2js.mjs examples/arr.calc | node
{ re: 6, im: 0 }
{ re: 333, im: 0 }
[ { re: 6, im: 0 }, { re: 333, im: 0 }, { re: 9, im: -1 } ]
What will you do when the index is out of bounds or not defined?
null
Add null
to the language and use it as the default value for undefined
indexes.
print(a(9)), # null
A Translation Scheme
Here is a possible translation scheme for the array notation:
âžś left-side-solution git:(debug) âś— bin/calc2js.mjs examples/arr2.calc
#!/usr/bin/env node
const { arr, Complex, print, assign } = require("/Users/casianorodriguezleon/campus-virtual/2425/pl2425/practicas/left-side/left-side-solution/src/support-lib.js");
/* End of support code */
let $a;
(((($a = arr(
Complex("4").add(Complex("2")),
Complex("5").add(Complex("3i")),
Complex("9").sub(Complex("i"))
),
print($a(Complex("0")))), // { re: 6, im: 0 }
assign($a, [Complex("1")], Complex("333"))),
print($a(Complex("1")))), // { re: 333, im: 0 }
print($a(Complex("9")))), // null
print($a); // [ { re: 6, im: 0 }, { re: 333, im: 0 }, { re: 9, im: -1 } ]
Tests
Add tests for the new functionality.
Challenge: Add an “Object-like notation” to the Calculator
Objective
Add an object notation to the language with a syntax like the illustrated in the example below:
a = { "a": 4+2; "b": 5+3i; "c": 9-i}, // Creates a new function "a" => 4+2, "b" => 5+3i, "c" => 9-i
print(a("a")), // { re: 6, im: 0 }
a("b") = 333,
print(a("b")) // { re: 333, im: 0 }
Where the assignment a = { "a": 4+2; "b": 5+3i; "c": 9-i}
creates a new function
a
that maps indexes to values: "a" => 4+2
, "b" => 5+3i
, "c" => 9-i
and otherwise
returns null
.
The execution of the code above should print an output like the following:
âžś left-side-solution git:(debug) âś— bin/calc2js.mjs examples/object.calc | node
{ re: 6, im: 0 }
{ re: 333, im: 0 }
null
What will you do when the “index” not defined?
Add null
to the language and use it as the default value for undefined
indexes.
print(a("d")), # null
A Translation Scheme
Here is a possible translation scheme for the object notation:
âžś left-side-solution git:(debug) âś— bin/calc2js.mjs examples/object2.calc
#!/usr/bin/env node
const { obj, Complex, print, assign } = require("/Users/casianorodriguezleon/campus-virtual/2425/pl2425/practicas/left-side/left-side-solution/src/support-lib.js");
/* End of support code */
let $a;
(((($a = obj({
"a": Complex("4").add(Complex("2")),
"b": Complex("5").add(Complex("3i")),
"c": Complex("9").sub(Complex("i"))
}),
print($a("a"))), // { re: 6, im: 0 }
assign($a, ["b"], Complex("333"))),
print($a("b"))), // { re: 333, im: 0 }
print($a("d"))) // null
Introduce the grammar and lexical rules for the object notation.
Write the obj
function that creates an object-like notation for the calculator language.
Tests
Add tests for the new functionality.
Challenge: Add an “On predicate assignment” to the Calculator (too difficult)
This is a challenge that can be the subject for the TFA.
Objective
Add an on predicate assignment to the language with a syntax like the illustrated in the example below:
f = fun(x) { x + 1},
even = fun(n) { n%2 == 0 }, // even is a predicate describing the set of even numbers
f(on even) = fun(x) { x*x }, // Syntax for modification in a whole set
print(f(2)), // 4 = 2*2 since 2 is even
print(f(1)) // 2 = 1+1 since 1 is odd
Where the assignment f(on even) = fun(x) { x*x }
modifies the function f
to return the square of the input z
when the predicate even(z)
is true
,
and z+1
otherwise.
The execution of the code above should print an output like the following:
âžś left-side-solution git:(debug) âś— bin/calc2js.mjs examples/on-predicate.calc | node
{ re: 4, im: 0 }
{ re: 2, im: 0 }
Semantic when predicates Intersect
When two predicates intersect, the last assignment prevails:
even = fun(n) { n%2 == 0 }, // even is a predicate describing the set of even numbers
multipleOf3 = fun(n) { n%3 == 0 }, // multipleOf3 is a predicate describing the set of numbers multiple of 3
f(on even) = 4,
f(on multipleOf3) = 9,
print(f(6)) // 9 The last assignment always prevails
Nested on predicates
When a function returns a function, the on predicate
can be nested:
f = fun(x) { fun(y) { x + y } },
even = fun(n) { n%2 == 0 }, # The set of even numbers
odd = fun(n) { n%2 != 0 }, # The set of odd numbers
f(on even)(on odd) = fun(x) { fun(y) { x*y } }, # Nested predicates: x in even && y in odd
print(f(4)(5)), # 20 since 4 is even and 5 is odd
print(f(3)(5)) # 8 since 3 is not even
print(f(4)(6)) # 10 since 4 is even and 6 is not odd
Consequences
I believe this will be the behavior on an array:
even = fun(n) { n%2 == 0 }, # The set of even numbers
odd = fun(n) { n%2 != 0 }, # The set of odd numbers
a = [8,7,6,5,4,3,2,1],
c = fun(x) { x + 3 },
b = a(on even),
b = c(on odd),
print(b(0)), // 8
print(b(1)), // 4 = 1+3
print(b(2)), // 6
Tests
Add tests for the new functionality.
Augmenting indexation: Finite vs Infinite Data Structures
From the early days of language programming design, indexable data structures have been
restricted to value-semantic (finite) structures like integers, strings, and booleans.
This is why in most languages you have expressions like a[0]
or a["key"]
but there are
no data structures that can be indexed with a function like a[x => x*x]
or even on an array
a[[3,2]] = 4
or a[{x :5}]
. Even indexing in a float is risky because of the
inherent nature of how floating-point arithmetic works.
One idea here is to extend the equality ”==” operator so that we can differentiate JSON-like arrays and objects and those are compared by value:
a = [4+2;5+3i;[9-i]], //JSONABLE function
b = [6;5+3i;[3*3-i]], // Another JSONABLE: a == b in "deep equality"
print(a == b), // true
If we implement assignable functions using a cache that computes the key/elements so that finite JSON-like objects that are equal are mapped to the same cache entry, then a program like the following:
a = [4+2;5+3i;[9-i]],
b = [6;5+3i; [9-i]], // a == b in "deep equality"
f = fun(x) { x + 1 },
f(a) = 4,
print(f(b)), // 4. Since the entry for "b" in the cache of "f" is the same than for "a"
will print 4
instead of function ...
(since when using identity f(b) = [6;5+3i; [9-i]]+1
will be a function 0 => 7, 1 => 6+3i, 2 => [9-i]+1
.
To differentiate between finite (JSONABLE) and infinite (non-JSONABLE) data structures, we need some way to mark those structures in the language.