docs: def link(origin=None, rel=None, value=None, attributes=None, source=None):
      '''
      Action function generator to create a link based on the context's current link, or on provided parameters
  
      :param origin: IRI/string, or list of same; origins for the created relationships.
      If None, the action context provides the parameter.
  
      :param rel: IRI/string, or list of same; IDs for the created relationships.
      If None, the action context provides the parameter.
      
      :param value: IRI/string, or list of same; values/targets for the created relationships.
      If None, the action context provides the parameter.
      
      :param source: pattern action to be executed, generating contexts to determine the output statements. If given, overrides specific origin, rel or value params
  
      :return: Versa action function to do the actual work
      '''
      attributes = attributes or {}
      #rel = I(iri.absolutize(rel, ctx.base))
      def _link(ctx):
          if source:
              if not callable(source):
                  raise ValueError('Link source must be a pattern action function')
              contexts = source(ctx)
              for ctx in contexts:
                  ctx.output_model.add(ctx.current_link[ORIGIN], ctx.current_link[RELATIONSHIP], ctx.current_link[TARGET], attributes)
              return
  
          (o, r, v, a) = ctx.current_link
          _origin = origin(ctx) if callable(origin) else origin
          o_list = [o] if _origin is None else (_origin if isinstance(_origin, list) else [_origin])
          #_origin = _origin if isinstance(_origin, set) else set([_origin])
          _rel = rel(ctx) if callable(rel) else rel
          r_list = [r] if _rel is None else (_rel if isinstance(_rel, list) else [_rel])
          #_rel = _rel if isinstance(_rel, set) else set([_rel])
          _value = value(ctx) if callable(value) else value
          v_list = [v] if _value is None else (_value if isinstance(_value, list) else [_value])
          #_target = _target if isinstance(_target, set) else set([_target])
          _attributes = attributes(ctx) if callable(attributes) else attributes
  
          #(ctx_o, ctx_r, ctx_t, ctx_a) = ctx.current_link
  
          #FIXME: Add test for IRI output via wrapper action function
          for (o, r, v, a) in [ (o, r, v, a) for o in o_list for r in r_list for v in v_list ]:
              ctx.output_model.add(o, r, v, attributes)
  
          return
      return _link