def difference(self, other, joinBy=None, exact=False):
        """
        *Wrapper of* ``DIFFERENCE``

        DIFFERENCE is a binary, non-symmetric operator that produces one sample
        in the result for each sample of the first operand, by keeping the same
        metadata of the first operand sample and only those regions (with their
        schema and values) of the first operand sample which do not intersect with
        any region in the second operand sample (also known as negative regions)
        
        :param other: GMQLDataset
        :param joinBy: (optional) list of metadata attributes.  It is used to extract subsets of samples on which
                       to apply the operator: only those samples in the current and other dataset that have the same
                       value for each specified attribute are considered when performing the operation
        :param exact: boolean. If true, the the regions are considered as intersecting only if their coordinates
                      are exactly the same
        :return: a new GMQLDataset


        Example of usage. We compute the exact difference between Example_Dataset_1 and Example_Dataset_2,
        considering only the samples with same `antibody`::

            import gmql as gl

            d1 = gl.get_example_dataset("Example_Dataset_1")
            d2 = gl.get_example_dataset("Example_Dataset_2")

            result = d1.difference(other=d2, exact=True, joinBy=['antibody'])
        """

        if isinstance(other, GMQLDataset):
            other_idx = other.__index
        else:
            raise TypeError("other must be a GMQLDataset. "
                            "{} was provided".format(type(other)))

        if isinstance(joinBy, list) and \
            all([isinstance(x, str) for x in joinBy]):
            metaJoinCondition = Some(self.opmng.getMetaJoinCondition(joinBy))
        elif joinBy is None:
            metaJoinCondition = none()
        else:
            raise TypeError("joinBy must be a list of strings. "
                            "{} was provided".format(type(joinBy)))

        if not isinstance(exact, bool):
            raise TypeError("exact must be a boolean. "
                            "{} was provided".format(type(exact)))

        new_index = self.opmng.difference(self.__index, other_idx, metaJoinCondition, exact)

        new_local_sources, new_remote_sources = self.__combine_sources(self, other)
        new_location = self.__combine_locations(self, other)
        return GMQLDataset(index=new_index, location=new_location,
                           local_sources=new_local_sources,
                           remote_sources=new_remote_sources,
                           meta_profile=self.meta_profile)