On 11 Jun 2019, at 09:50, Yanghao Hua <yanghao.py@gmail.com> wrote:

On Mon, Jun 10, 2019 at 8:57 PM Caleb Donovick <donovick@cs.stanford.edu> wrote:

First off, I have admittedly not read all of this thread.  However, as designer of DSL's in python, I wanted to jump in on a couple of things I have seen suggested.  Sorry If I am repeating comments already made.  Over the last few years I have thought about every suggestion I have seen in this thread and they all don't work or are undesirable for one reason or another.

Glad to see I am not alone in the woods :-) So the argument that this
problem is only Yanghao -- a single person's problem -- can be gone.
You have to rephrase it to "this is only two person's problem" now.
;-)

Some times a DSL is usable within the python syntax and that is great.
I have use python for a number of DSL's.

But when the DSL is beyond what python can help with directly I'm wondering
why you do not parse the DSL with python and execute the results.

In that way you can have any semantics that you want from any syntax that you
wish to have. However you do not need the python language to be changed at all.

And it must be clear that you are making little to no progress on convincing people
that changing python is a good idea.

Barry



Regarding what code will become simpler if an assignment operator was available.  I currently walk the AST to rewrite assignments into the form I want.  This is code is really hard to read if you are not familiar with the python AST, but the task it is performing is not hard to understand at all (replace assignment nodes with calls to a function call to dsl_assign(target_names, value, globals(), locals()).  The dsl_assign function basically performs some type checking before doing the assignment.  Once again the code is much harder to understand than it should be as it operates on names of variables and the globals / locals dictionaries instead of on the variables themselves.  Also to get the hook into the AST I have to have use an importer which further obscures my code and makes use kinda annoying as one has to do the following:
```
main.py:
import dsl # sets up the importer to rewrite the AST
import dsl_code # the code which should morally be the main but most be imported after dsl
```
Granted anything in a function or a class can be rewritten with a decorator but module level code must be rewritten by an importer.

I thought about doing it a lot ... eventually manipulating AST is not
so much easier than actually re-develop the entire DSL from scratch
... and this suffers eventually similar isssues like @=/L[:]
overriding, it abuses a common understanding. I have also been looking
into MacroPy3 for some time now, and despite you still need something
like P[your customized expression] (the P[...] overhead which is
exposed to end-users), changing an existing python syntax to mean
something completely different, or translating a non-exist python
syntax into something else really makes me feel this will make things
mentally inconsistent, and actually makes python no longer python ...
And this haven't touched what it means for debugging later on ...

The problem with overloading obj@=value:
As Yanghao have pointed out @ comes with expectations of behavior.  Granted I would gamble most developers are unaware that @ is python operator but it still has meaning and as such I don't like abusing it.

Yep.

The problem with using obj[:]=value:
Similar to @ getitem and slices have meaning which I don't necessarily want to override. Granted this is least objectionable solution I have seen although, it creates weird requirements for types that have __getitem__ so it can just be used by inheriting `TypedAssignment` or something similar.

Exactly. I will try to summarize all the pros and cons I saw people
posting for the L[:] case, and the thing for DSL is L[:] is confusing
on its own (not better than obj.next = ...).

The problem with descriptors:
They are hard to pass to functions, for example consider trying to fold assignment.
```
signals = [Signal('x'), Signal('y'), Signal('z')]
out = functools.reduce(operator.iassign, signals)
```
vs
```
signals = SignalNamespace()
signal_names =  ['x', 'y', 'z']
out = functools.reduce(lambda v, name: settattr(signals, name, v))
```
In general one has to pass the name of the signal and the namespace to a function instead the signal itself which is problematic.

The problem with exec:
First off its totally unpythonic, but even if I hide the exec with importer magic it still doesn't give me the behavior I want.
Consider the following
```
class TypeCheckDict(dict, MutableMapping): # dict needed to be used as globals
 """
 Dictionary which  binds keys to a type on first assignment then type checks on future
 assignement. Will infer type if not already bound. """
 __slots__ = '_d'
 def __init__(self, d=_MISSING):
   if d is _MISSING:
     d = {}
   self._d = d

 def __getitem__(self, name):
   v = self._d[name][1]
   if v is _MISSING:
     raise ValueError()
   else:
     return v

 def __setitem__(self, name, value):
   if name not in self._d:
     if isinstance(value, type):
       self._d[name] = [value, _MISSING]
     else:
       self._d[name] = [type(value), _MISSING]
     elif isinstance(value, self._d[name][0]):
       self._d[name][1] = value
     else:
       raise TypeError(f'{value} is not a {self._d[name][0]}')

 # __len__ __iter__ __delitem__ just dispatch to self._d

S = '''
x = int
x = 1
x = 'a'
'''
exec(S, TypeCheckDict(), TypeCheckDict()) # raises TypeError 'a' is not a int

S = '''
def foo(): # type of foo inferred
 x = int
 x = 'a'
foo()
'''
exec(S, TypeCheckDict(), TypeCheckDict()) # doesn't raise an error as a normal dict is used in foo
```

Python provides us all the flexibility to do all kinds of "fancy"
things, so flexible that we can make it *NOT* look like python at all.
I have done similar things for descriptors, and I am still thinking
was it the right approach to make descriptor not behaving like the way
a descriptor is supposed to behave.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UHSLG7JWXXRRAXS2JCFKV656TFO3QRUE/
Code of Conduct: http://python.org/psf/codeofconduct/