Skip to content
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

Do lazy evaluation in list/map index defaults #20

Closed
lslezak opened this issue Jun 11, 2013 · 8 comments
Closed

Do lazy evaluation in list/map index defaults #20

lslezak opened this issue Jun 11, 2013 · 8 comments
Labels

Comments

@lslezak
Copy link
Member

lslezak commented Jun 11, 2013

The defaults for accessing YCPList and YCPMap values need to evaluated only when the requested index/key is missing.

If a function with side effect is used for returning the default then the Ruby code behaves differently to the original YCP.

YCP Example:

{
  boolean flag = false;

  define string Foo()
  {
    flag = true;
    return "foo";
  }

  map<string,string> m = $["a" : "b"];

  string str = m["a"]:Foo();
  y2internal("flag: %1", flag);

  str = m["missing"]:Foo();
  y2internal("flag: %1", flag);
}

This logs:

flag: false
flag: true

The converted ruby code contains this

 @str = Ops.index(@m, ["a"], Foo())

which causes the Foo() function evaluation before accessing the map and thus logs:

flag: true
flag: true

This breaks testsuite in yast2 in PortAliases module, see lines https://github.com/yast/yast-yast2/blob/master/library/network/src/PortAliases.ycp#L216 and https://github.com/yast/yast-yast2/blob/master/library/network/src/PortAliases.ycp#L167 where the default handler function modifies the accessed map (yeah!).

@lslezak
Copy link
Member Author

lslezak commented Jun 14, 2013

I have added a workaround in yast/ycp-killer#461, it should be removed after fixing this issue.

@dmajda
Copy link
Contributor

dmajda commented Jun 20, 2013

@jreidinger Could you make Ops.index to accept an additional way to specify the default value — via a block? The block would be called only when the default is really needed (the looked-up element is missing). If no block is passed, the 3rd argument would be used as default, as it is now.

With that implemented, I could translate this code:

string str = m["a"]:Foo();

as:

@str = Ops.index(@m, ["a"]) { Foo() }

This would ensure lazy evaluation of the default value. Translation of default values that are not functions would stay the same (they would be passed as the 3rd argument).

@jreidinger
Copy link
Member

yes, WIP, not sure if I finish it today

@jreidinger
Copy link
Member

Implemented - yast/yast-ruby-bindings@0dfcb4d

@dmajda dmajda closed this as completed in b1cfc60 Jun 21, 2013
@dmajda
Copy link
Contributor

dmajda commented Jun 21, 2013

One note to the fix: It only fixes the case when the default value is a call. All other cases are still evaluated eagerly. This means that e.g. complex default values including calls (e.g. a list or map) may still behave incorrectly.

I don't look if such values are even allowed in YCP, and how they behave if so. Moreover, I didn't see any such default value in the code so far. As a result, I think this case is most likely irrelevant.

@mvidner
Copy link
Member

mvidner commented Jun 21, 2013

Sorry, that argument is wrong. Yes, the majority of cases are simple values, but fixing only one specific case of evaluable values can result in subtle, well hidden bugs. It should be safe and equally simple to replace "use block if default is a call" with "use block if default is not a literal".

To clarify, $[] is a literal, $["foo": "bar"] is still a literal, and $["foo": Bar()] is not. But it would be OK to treat only empty maps as "simple" defaults.

@mvidner mvidner reopened this Jun 21, 2013
dmajda added a commit that referenced this issue Jul 8, 2013
This change avoids subtle bugs when a there is a function hidden
somewhere in the default value.

Note that we still evaluate empty lists, maps, and terms non-lazily as
there could be no function hidden inside them.

This extends previous fix of #20.
@dmajda
Copy link
Contributor

dmajda commented Jul 8, 2013

@mvidner I added lazy evaluation for non-empty lists, maps and terms. It seems a reasonable compromise between generating too many blocks and too complex code. We can improve this further later if we'll have time.

BTW, this change added 145 blocks to the translated YaST code, most of them containing UI terms (e.g. VSpacing(1)). This seems acceptable to me.

@dmajda dmajda closed this as completed Jul 8, 2013
@dmajda
Copy link
Contributor

dmajda commented Jul 8, 2013

BTW, this change added 145 blocks to the translated YaST code, most of them containing UI terms (e.g. VSpacing(1)).

Correction: There were 145 + lines in the diff. That doesn't mean 145 blocks, because some added blocks were multi-line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants