turn function add(1, 4) into (add, 1, 4)

Question:

I have been experimenting with using functions to make a mini programming language, but can’t find out how to turn the function

add(1, 4)

into

(add, 1, 4)

So far I have this:

mem = {}

def inp(text):
    return(input(text))

def store(name, value):
    mem[str(name)] = value

def get(name):
    return(mem[str(name)])

def ifel(con, exp1, exp2):
    if con == True:
        return(exp1)
    else:
        return(exp2)

def add(*args):
    return(sum(args))

def sub(*args):
    last = 0
    for each in args:
        last = last - each
    return(last)

def dev(*args):
    last = 0
    for index, each in enumerate(args):
        if index == 0:
            last = each
        else:
            last = last / each
    return(last)

def mul(*args):
    last = 0
    for index, each in enumerate(args):
        if index == 0:
            last = each
        else:
            last = last * each
    return(last)

def power(*args):
    last = 0
    for index, each in enumerate(args):
        if index == 0:
            last = each
        else:
            last = pow(last, each)
    return(last)

def root(*args):
    last = 0
    for index, each in enumerate(args):
        if index == 0:
            last = each
        else:
            last = pow(last, (1 / each))
    return(last)

def say(t):
    print(t)

print((5 * (9 ** 4)) / 3)
say(dev(mul(5, power(9, 4)), 3))

I would like to be able to write this as:
(say, (dev, (mul, 5, (power, 9, 5)), 3))
or
(say (dev (mul 5 (power 9 5)) 3))

How could I call a function with the first place in the parentheses?
I got inspiration from Lisp syntax, and am trying to recreate my own version.

Asked By: Adras Baros

||

Answers:

You’re either making a fairly basic mistake or attempting something very nuanced and difficult. 🙂

Assuming the former: You need to be clear about the difference between python syntax and your syntax. Functions in your syntax are generally not going to be python functions (there’s some nuance there) – they’ll be values, and you’ll have something like an eval(...) function that actually interprets them.

Because python functions are values, there can be some overlap here – (add, 3, 4) could be treated as a tuple whose first value is a function. Pretending all your functions were binary operations, you could have something like:

def eval(someFunction, arg1, arg2):
   someFunction(eval(arg1), eval(arg2))

with some other code to handle the base case of literal values. (Or do varargs and have literals as 0-argument functions that return the literal.)

More commonly you’d just have add, etc, by something more akin to an enum, and eval would match on each case:

def eval(term):
   match term:
       case(ADD, arg1, arg2): return eval(arg1) + eval(arg2)

Generally speaking, this will be easier to serialize and inspect than having actual lambdas.

The Advanced Thing:

You’re almost certainly not doing this, but there is such a thing as a "quoted DSL" which is a domain-specific language written as an extension of the host language. Implementing them is much more involved, but they have the advantage of benefiting from the development environment and features of the host language. (For instance, you can use them to get syntax completion and error highlighting in your DSL without taking on the very difficult task of writing such yourself.)

Answered By: Edward Peters