-
Notifications
You must be signed in to change notification settings - Fork 492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ambiguous glob semantics #1724
Comments
I understand the problem of targeting vs creating, and I understand that new syntax can disambiguate. However, it's a design decision of the language to stick with readability. I think the Let's say I want all inner node
It's connections that have the property of creating when it doesn't exist, not globs. E.g., if you write I suppose any filter on the source would only match existing nodes. **.server -> lb: {
&src.label: *
} That doesn't seem overly verbose. |
My diagrams contain parts like these: # define lots of containers with imports from my component library, e.g.
step_1 {
forms/enter_credentials_1: ...@forms/enter_credentials
# ... more similar imports
}
step_2 {
processes/create_account_1: ...@processes/create_account
# ... more similar imports
}
# ... more similar steps
# define lots of container-agnostic edges between imports:
*.*forms/enter_credentials_1.out.temporary_state -> *.*processes/create_account_1.in.temporary_state
# ... a lot more of these edges I currently have 20+ occurrances of the last line. That's the old and small version of the diagram. The finished one will have close to 100 edges. I don't want to write the following 100 times: *.forms/enter_credentials_1.out.temporary_state -> *.processes/create_account_1.in.temporary_state : {
&src.label: *
&dst.label: *
} Do filters also work with imports? Then I could separate the block out into a separate file *.forms/enter_credentials_1.out.temporary_state -> *.processes/create_account_1.in.temporary_state : @if_both_exist That would be better, but still not optimal. Because if I would also want to style the edges, that would get complicated. Where do I put the styles in this version? An explicit syntax for the globs let's me use the import for the things that actually are important to me, e.g. *?.forms/enter_credentials_1.out.temporary_state -> *?.processes/create_account_1.in.temporary_state : { class: [success] }
*?.forms/enter_credentials_1.out.temporary_state -> *?.forms/enter_credentials_1.in.temporary_state : { class: [error] } EDIT: Is the following possible? *.forms/enter_credentials_1.out.temporary_state -> *.processes/create_account_1.in.temporary_state : { class: [success]; ...@if_both_exist }
*.forms/enter_credentials_1.out.temporary_state -> *.forms/enter_credentials_1.in.temporary_state : { class: [error]; ...@if_both_exist } That would be getting close to acceptable. Still, it's a lot of redundancy, because I need this EDIT 2: Can classes apply filters? Is the following possible *.forms/enter_credentials_1.out.temporary_state -> *.processes/create_account_1.in.temporary_state : { class: [success; edge] }
*.forms/enter_credentials_1.out.temporary_state -> *.forms/enter_credentials_1.in.temporary_state : { class: [error; edge] } With: classes: {
success.stroke: green
error.stroke: red
edge {
&src.label: *
&dst.label: *
}
} |
I tried the following in the playground: a: {aa}
b: {bb}
*.aa -> *.bb: { class: [edge] }
classes: {
edge: {
&src.label: *
&dst.label: *
}
} I got the error "glob filters cannot be used outside globs". This breaks both my attempted solutions, classes as well as imports. Could you make this error happen lazily? So if a block defines a glob filter and is only used within globs, could you make this not raise an error and work as intended? Also, it might be in better accordance with your desing goals (https://d2lang.com/tour/design#warnings--errors) to ignore these glob filters where not applicable and output a warning instead of raising an error and terminating. EDIT: I tried the following locally and even that doesn't work: *.unique_name_1 -> *.unique_name_2: {
&src.label: *
&dst.label: *
class: [success]
} I also get "glob filters cannot be used outside globs". So your currently proposed solution is actually a syntax error right now. One more question: are there any |
Thanks for the fix in #1712
However, it seems to not fix globs in general. For example
**.*a -> **.*b
still generates new nodes.I'm still not sure, whether I like the semantics of globs in D2. For me a glob matches existing things. A glob suddenly creating new things feels weird. Probably that's super useful within D2 and I just didn't run into any of these use cases, yet. But if globs create new nodes, how would you even avoid infinite recursion in all cases? This seems like a slippery slope to me.
I guess my issue here is that there is no clear delimiter where the glob ends and the code starts. I'll denote the "glob" with square brackets.
Let's say I actually want to create a node
a
for all existing containers. I'd want to write:Let's say I only wanted to match existing nodes
a
within containers. I'd want to write:The bracket syntax does not exist in D2. These look the same in D2 currently and I would need to resort to filters or this "hacky" syntax
This works as a temporary workaround, but it doesn't actually express my intent. I don't want to match nodes that "end in
a
". I want those that are "exactlya
".Maybe filters will provide a solution that covers all edge-cases, but please also consider that they are overly verbose.
Consider introducing an explicit syntax for "the glob ends here". Probably we don't even need brackets as a glob always needs to start at the beginning of the expression to my understanding.
So maybe you could even use something very unobtrusive like
..
:Create new nodes "
a
with content" as well asb
andc
connected by an edgeMatch existing nodes
a
and add content, match existing nodesb
andc
and add edge:I chose the
..
syntax first, becaus it doesn't use any new symbols. If it doesn't clash with the rest of the syntax, we could also use?.
. That feels even more consistent with the edge syntax:b -> c
b
exists, createc
and a connecting edge:b? -> c
b
andc
exist, create an edge:b? -> c?
The
?
would just mean "the glob ends here" and would be usable both for further chaining with.
as well as for creating edges.This would bring a new default behaviour:
a
would not be a glob (unless explicitly turned into one bya..
ora?
) and would always create a new node. I guess that's what a user expects*
would by default be a glob as a whole (unless the user explicitly terminates the glob early with a?
or implicitly with curly braces) and would never create new nodes. I guess that's also reasonableThe text was updated successfully, but these errors were encountered: