partial function evaluates... when?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
Hakkie
Newbie
Posts: 10
Joined: Sat Dec 02, 2017 7:13 am
Contact:

partial function evaluates... when?

#1 Post by Hakkie »

I'm probably missing something obvious, but I can't figure it out myself.
I've isolated my problem to this short script below:

Code: Select all

init python:
    from functools import partial
    class Book(object):
        def __init__(self, reqs=[]):
            self.reqs = reqs
        def canUse(self):
            if checkReqs(self.reqs):
                return True
            return False

    def checkReqs(reqs):
        for _req in reqs:
            if not _req():
                return False
        return True
    def isTrue(bool):
        if bool is True:
            return True
        return False

default allowreading = False
default mybook = Book(reqs=[partial(isTrue, allowreading)])

label start:
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'False' (and that is correct!)

    $ allowreading = True
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'False' (But... why? Shouldn't the partial-function evaluate to 'True' now?)

    $ mybook.reqs=[partial(isTrue, allowreading)] # lets try updating the book.reqs value with exactly the same as I initialised it
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'True' (why does this work? I've supplied exactly the same partial-function like before)
    return
I really don't understand why the function 'mybook.canUse() doesnt return 'True' after I've set the allowreading variable to 'True'.
But, it does return True after I've supplied exactly the same reqs.... please help, I'm going nuts. :)

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2402
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: partial function evaluates... when?

#2 Post by Ocelot »

A rough example how partial application works:

Code: Select all

class Partial:
    def __init__(self, func, *args):
        self.function = func
        self.args = tuple(args)

    def __call__(self):
        return function(*args)
What is important is that arguments are captured at moment of partilly applied function creation and are not updated later (this is slightly more complex for reference types, where if you change referenced object itself, changes would be reflected when it is accessed through captured reference).

your code is equivalent to the following:

Code: Select all

label start:
    # default objects are initialized right after start.
    $ mybook = Book(reqs=[partial(isTrue, False)]) # Boolean objects are singletons, so you can substitute their value directly
    # Partial knows nothing about variables and varuable names. The only thing it cares about are values.

    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'False' (and that is correct!)

    $ allowreading = True # Value stored within partial is still False.
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'False' (But... why? Shouldn't the partial-function evaluate to 'True' now?)
    # No, because you should change reference stored into partial function, not completely unrelated one.


    # $ mybook.reqs=[partial(isTrue, allowreading)] # lets try updating the book.reqs value with exactly the same as I initialised it
    $ mybook.reqs=[partial(isTrue, True)] # Lets do the same substitution we did originally.
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'True' (why does this work? I've supplied exactly the same partial-function like before)
    # It returns different value because it is a different function, with different arguments supplied to it.
    return
< < insert Rick Cook quote here > >

Hakkie
Newbie
Posts: 10
Joined: Sat Dec 02, 2017 7:13 am
Contact:

Re: partial function evaluates... when?

#3 Post by Hakkie »

Ah, thank you!
That made it a lot clearer, because I felt that it worked 'sometimes', but in those cases I did something like this:

Code: Select all

init python:
    from functools import partial
    class Book(object):
        def __init__(self, reqs=[]):
            self.reqs = reqs
        def canUse(self):
            if checkReqs(self.reqs):
                return True
            return False

    class Player(object):
        def __init__(self):
            self.allowreading = False

    def checkReqs(reqs):
        for _req in reqs:
            if not _req():
                return False
        return True
    def canPlayerRead(player):
        if player.allowreading is True:
            return True
        return False

default player = Player()
default mybook = Book(reqs=[partial(canPlayerRead, player)]) # here I'm only giving a reference, and that's why it can work, right?

label start:
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Return 'False' and is correct!

    $ player.allowreading = True
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'True' and is correct!
    return
In this example, I'm supplying a reference to an object, and then it does evaluate during runtime.
But, if I had supplied it 'player.allowreading', then it wouldn't be a reference anymore and wouldn't work.

Is there a better way of storing requirements inside a object/variable? And then evaluate during runtime when needed?
Or, is there a way to supply the partial-function a reference to a variable?

Because I still quite like the idea of being able to add a whole bunch of 'requirements' like this and evaluate anywhere and anytime I want in game.

Hakkie
Newbie
Posts: 10
Joined: Sat Dec 02, 2017 7:13 am
Contact:

Re: partial function evaluates... when?

#4 Post by Hakkie »

Thinking about it a bit more, this seems to work:

Code: Select all

init python:
    from functools import partial
    class Book(object):
        def __init__(self, reqs=[]):
            self.reqs = reqs
        def canUse(self):
            if checkReqs(self.reqs):
                return True
            return False

    class GameVariable(object):
        def __init__(self, value):
            self.value = value

    def checkReqs(reqs):
        for _req in reqs:
            if not _req():
                return False
        return True
    def isTrue(gamevariable):
        if gamevariable.value is True:
            return True
        return False

default allowreading = GameVariable(False)
default mybook = Book(reqs=[partial(isTrue, allowreading)])

label start:
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'False' (and that is correct!)

    $ allowreading.value = True
    $ _tmp = mybook.canUse()
    "Can I read the book? -> [_tmp]" # Returns 'True', (whoohoo!)

    return
So, instead of using variables, I define an object that holds a variable.
I'm not sure if this is the smartest workaround for my issue though.

But, I'm happy to have learned something new today, much appreciated Ocelot!

Post Reply

Who is online

Users browsing this forum: juliaa__