<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def from_call(foo, args, kwargs, hints, call_by_value=True):
 """Takes a function and a set of arguments it needs to run on. Returns a newly constructed workflow representing the promised value from the evaluation of the function with said arguments. These arguments are stored in a BoundArguments object matching to the signature of the given function ``foo``. That is, bound_args was constructed by doing:: inspect.signature(foo).bind(*args, **kwargs) The arguments stored in the ``bound_args`` object are filtered on being either plain, or promised. If an argument is promised, the value it represents is not actually available and needs to be computed by evaluating a workflow. If an argument is a promised value, the workflow representing the value is added to the new workflow. First all the nodes in the original workflow, if not already present in the new workflow from an earlier argument, are copied to the new workflow, and a new entry is made into the link dictionary. Then the links in the old workflow are also added to the link dictionary. Since the link dictionary points from nodes to a :py:class:`set` of :py:class:`ArgumentAddress` es, no links are duplicated. In the ``bound_args`` object the promised value is replaced by the ``Empty`` object, so that we can see which arguments still have to be evaluated. Doing this for all promised value arguments in the bound_args object, results in a new workflow with all the correct dependencies represented as links in the graph. :param foo: Function (or object) being called. :type foo: Callable :param args: Normal arguments to call :param kwargs: Keyword arguments to call :param hints: Hints that can be passed to the scheduler on where or how to schedule this job. :returns: New workflow. :rtype: Workflow """

    # create the bound_args object
    bound_args = signature(foo).bind(*args, **kwargs)
    bound_args.apply_defaults()

    # get the name of the variadic argument if there is one
    variadic = next((x.name
                     for x in bound_args.signature.parameters.values()
                     if x.kind == Parameter.VAR_POSITIONAL), None)

    # *HACK*
    # the BoundArguments class uses a tuple to store the
    # variadic arguments. Since we need to modify them,
    # we have to replace the tuple with a list. This works, for now...
    if variadic:
        if variadic not in bound_args.arguments:
            bound_args.arguments[variadic] = []
        else:
            bound_args.arguments[variadic] = \
                list(bound_args.arguments[variadic])

    # create the node and initialise hash key
    node = FunctionNode(foo, bound_args, hints)

    # setup the new workflow
    root = id(node)
    nodes = {root: node}
    links = {root: set()}

    # walk the arguments to the function call
    for address in serialize_arguments(node.bound_args):
        arg = ref_argument(node.bound_args, address)

        # the argument may still become a workflow if it
        # has the __deepcopy__ operator overloaded to return a workflow
        call_by_ref = 'call_by_ref' in hints and \
            (hints['call_by_ref'] is True or
             address.name in hints['call_by_ref'])

        if not is_workflow(arg) and call_by_value and not call_by_ref:
            arg = deepcopy(arg)

        # if still not a workflow, we have a plain value!
        if not is_workflow(arg):
            set_argument(node.bound_args, address, arg)
            continue

        # merge the argument workflow into the new workflow
        workflow = get_workflow(arg)
        set_argument(node.bound_args, address, Empty)
        for n in workflow.nodes:
            if n not in nodes:
                nodes[n] = workflow.nodes[n]
                links[n] = set()

            links[n].update(workflow.links[n])

        links[workflow.root].add((root, address))

    return Workflow(root, nodes, links)