Python and Viv · Basics
Functions
What to notice
- Viv has no default arguments and no keyword arguments. The Viv side writes the general function with every parameter (
greet_with) and a short one that fills in the default (greet). - A docstring works the same way: a bare string as the first statement of a
defdocuments it and does nothing when it runs. return lo, hi, meanandlo, hi, mean = f(xs)are the same in both. In Viv the unpacking must be a statement of its own:print(f(xs))is refused.- A lambda is
fn(x) -> exprinstead oflambda x: expr. Adefname and a lambda are both values that can be passed and called. min,maxandsumtake an array in Viv as they take a list in Python.- Python's
print(lo, hi, mean)writes each float in full; the Viv side usesfmt(...)to get the same digits (see Printing and formatting).
Python functions.py · 38 lines
Viv functions.viv · 49 lines
py2viv translated from functions.py
What py2viv adds before the program
(nothing)
(nothing)
63 lines: a header and the guard functions its translation calls
1###
2Translated from functions.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: ecc2eebd61cb15540a9c15d25b67cd0df8fd886b44a0f263632499f7ef242022
4Each print(...) is one print(...) line; fmt(x) writes a float as Python's repr does.
5A `raise`, a failed assert and every guard `stop` with Python's exception line.
6###
7
8#| 0, 1, ..., n - 1.
9def py_iota(n) {
10 a = zeros(n)
11 for i to n {
12 a[i] = i
13 }
14 return a
15}
16
17#| Every entry of a real array, row-major, as a vector (a copy).
18def py_flat(a: Arr) {
19 return take(a, py_iota(numel(a)))
20}
21
22#| a / b with a float: ZeroDivisionError on a zero divisor.
23def py_div_f(a, b) {
24 if b == 0 {
25 stop("ZeroDivisionError: float division by zero")
26 }
27 return a / b
28}
29
30#| CPython's builtin sum of a float array or list before 3.12: left to right from 0.0
31#| (Viv's own sum adds in NumPy's pairwise order).
32def py_lsum(a: Arr) {
33 s = 0.0
34 for i to len(a) {
35 s += a[i]
36 }
37 return s
38}
39
40#| Python's max over a sequence: the first entry, replaced by each later one that is greater
41#| (so a NaN first stays, a NaN later is skipped, and of -0.0 and 0.0 the first wins).
42def py_bmax(a: Arr) {
43 f = py_flat(a)
44 m = f[0]
45 for i from 1 to len(f) {
46 if f[i] > m {
47 m = f[i]
48 }
49 }
50 return m
51}
52
53#| Python's min over a sequence (the first of equals wins).
54def py_bmin(a: Arr) {
55 f = py_flat(a)
56 m = f[0]
57 for i from 1 to len(f) {
58 if f[i] < m {
59 m = f[i]
60 }
61 }
62 return m
63}
1 a function with a default argument
4def greet(name, greeting="hello"):
5 """A greeting for one person."""
6 return f"{greeting}, {name}!"
7
8print(greet("Ada"))
9print(greet("Grace", "hi"))
10print(greet("Alan", greeting="good morning"))
2def greet_with(name, greeting) {
3 "A greeting for one person."
4 return f"{greeting}, {name}!"
5}
6
7def greet(name) {
8 return greet_with(name, "hello")
9}
10
11print(greet("Ada"))
12print(greet_with("Grace", "hi"))
13print(greet_with("Alan", "good morning"))
68#| A greeting for one person.
69def greet(name, greeting) {
70 return greeting + ", " + name + "!"
71}
72print(greet("Ada", "hello"))
73print(greet("Grace", "hi"))
74print(greet("Alan", "good morning"))
2 several results
13def min_max_mean(xs):
14 lo = min(xs)
15 hi = max(xs)
16 return lo, hi, sum(xs) / len(xs)
17
18lo, hi, mean = min_max_mean([4.0, 1.0, 7.0, 2.0])
19print(lo, hi, mean)
16def min_max_mean(xs) {
17 lo = min(xs)
18 hi = max(xs)
19 return lo, hi, sum(xs) / len(xs)
20}
21
22lo, hi, mean = min_max_mean([4.0, 1.0, 7.0, 2.0])
23print(fmt(lo), fmt(hi), fmt(mean))
76def min_max_mean(xs: Arr) {
77 lo = py_bmin(xs)
78 hi = py_bmax(xs)
79 return lo, hi, py_div_f(py_lsum(xs), len(xs))
80}
81lo, hi, mean = min_max_mean([4.0, 1.0, 7.0, 2.0])
82print(fmt(lo), fmt(hi), fmt(mean))
3 a function as an argument
22def apply_twice(f, x):
23 return f(f(x))
24
25def square(x):
26 return x * x
27
28print(apply_twice(square, 3.0))
29print(apply_twice(lambda x: x + 10.0, 1.0))
26def apply_twice(f, x) {
27 return f(f(x))
28}
29
30def square(x) {
31 return x * x
32}
33
34print(fmt(apply_twice(square, 3.0)))
35print(fmt(apply_twice(fn(x) -> x + 10.0, 1.0)))
84def apply_twice(f, x) {
85 return f(f(x))
86}
87def square(x) {
88 return x * x
89}
90print(fmt(apply_twice(square, 3.0)))
91def _lam29(x) {
92 return x + 10.0
93}
94print(fmt(apply_twice(_lam29, 1.0)))
4 a lambda that captures a variable
32scale = 2.5
33scaled = lambda x: scale * x
34print(scaled(4.0))
38scale = 2.5
39scaled = fn(x) -> scale * x
40print(fmt(scaled(4.0)))
96scale = 2.5
97def scaled(x) {
98 return scale * x
99}
100print(fmt(scaled(4.0)))
5 checks
37assert greet("x") == "hello, x!"
38assert apply_twice(square, 2.0) == 16.0
43test "functions" {
44 assert_true(greet("x") == "hello, x!")
45 assert_true(apply_twice(square, 2.0) == 16.0)
46}
102if not (greet("x", "hello") == "hello, x!") {
103 stop("AssertionError")
104}
105if not (apply_twice(square, 2.0) == 16.0) {
106 stop("AssertionError")
107}
What they print
identical — the two programs print the same text.
Dimmed lines are left out of the comparison. Each Viv test block prints one PASS line; the Python checks the same facts with assert, which prints nothing.
$ python3 functions.py
hello, Ada!hi, Grace!good morning, Alan!1.0 7.0 3.581.021.010.0
$ viv run functions.viv
hello, Ada!hi, Grace!good morning, Alan!1.0 7.0 3.581.021.010.0PASS functions
$ viv run functions.py (py2viv’s translation)
The translation prints exactly what the Python prints.
hello, Ada!hi, Grace!good morning, Alan!1.0 7.0 3.581.021.010.0