docs: def apply(self, func, mapping=None, new_dtype=None, **kwargs):
          """Apply an element-wise UDF to the Series.
  
          There are currently 6 options for using a UDF. First 4 are lazy,
          other 2 are eager and require the use of the raw decorator:
  
          - One of the predefined functions in baloo.functions.
          - Implementing a function which encodes the result. kwargs are automatically passed to it.
          - Pure Weld code and mapping.
          - Weld code and mapping along with a dynamically linked C++ lib containing the UDF.
          - Using a NumPy function, which however is EAGER and hence requires self.values to be raw. Additionally, NumPy
              does not support kwargs in (all) functions so must use raw decorator to strip away weld_type.
          - Implementing an eager function with the same precondition as above. Use the raw decorator to check this.
  
          Parameters
          ----------
          func : function or str
              Weld code as a str to encode or function from baloo.functions.
          mapping : dict, optional
              Additional mappings in the weld_template to replace on execution.
              self is added by default to reference to this Series.
          new_dtype : numpy.dtype, optional
              Specify the new dtype of the result Series.
              If None, it assumes it's the same dtype as before the apply.
  
          Returns
          -------
          Series
              With UDF result.
  
          Examples
          --------
          >>> import baloo as bl
          >>> sr = bl.Series([1, 2, 3])
          >>> weld_template = 'map({self}, |e| e + {scalar})'
          >>> mapping = {'scalar': '2L'}
          >>> print(sr.apply(weld_template, mapping).evaluate())
          <BLANKLINE>
          ---  --
            0   3
            1   4
            2   5
          >>> weld_template2 = 'map({self}, |e| e + 3L)'
          >>> print(sr.apply(weld_template2).evaluate())
          <BLANKLINE>
          ---  --
            0   4
            1   5
            2   6
          >>> print(bl.Series([1., 4., 100.]).apply(bl.sqrt).evaluate())  # lazy predefined function
          <BLANKLINE>
          ---  --
            0   1
            1   2
            2  10
          >>> sr = bl.Series([4, 2, 3, 1])
          >>> print(sr.apply(bl.sort, kind='q').evaluate())  # eager wrapper over np.sort (which uses raw decorator)
          <BLANKLINE>
          ---  --
            0   1
            1   2
            2   3
            3   4
          >>> print(sr.apply(bl.raw(np.sort, kind='q')).evaluate())  # np.sort directly
          <BLANKLINE>
          ---  --
            0   1
            1   2
            2   3
            3   4
          >>> print(sr.apply(bl.raw(lambda x: np.sort(x, kind='q'))).evaluate())  # lambda also works, with x = np.array
          <BLANKLINE>
          ---  --
            0   1
            1   2
            2   3
            3   4
  
          # check tests/core/cudf/* and tests/core/test_series.test_cudf for C UDF example
  
          """
          if callable(func):
              return Series(func(self.values,
                                 weld_type=self.weld_type,
                                 **kwargs),
                            self.index,
                            self.dtype,
                            self.name)
          elif isinstance(func, str):
              check_type(mapping, dict)
              check_dtype(new_dtype)
  
              default_mapping = {'self': self.values}
              if mapping is None:
                  mapping = default_mapping
              else:
                  mapping.update(default_mapping)
  
              if new_dtype is None:
                  new_dtype = self.dtype
  
              return Series(weld_udf(func,
                                     mapping),
                            self.index,
                            new_dtype,
                            self.name)
          else:
              raise TypeError('Expected function or str defining a weld_template')