from typing import Dict
from typing import Any
from typing import Optional


def to_dot(
    config: Dict[str, Any],
    prefix: Optional[str] = None,
    separator: str = '.',
    function_to_name: bool = True,
) -> Dict[str, Any]:
    """Convert nested dictionary to flat dictionary.

    :param config:
        The potentially nested dictionary.
    :param prefix:
        An optional prefix.
    :param separator:
        The separator used to flatten the dictionary.
    :param function_to_name:
        Whether to convert functions to a string representation.

    :return:
        A flat dictionary where nested keys are joined by a separator.
    """
    result = dict()
    for k, v in config.items():
        if prefix is not None:
            k = f'{prefix}{separator}{k}'
        if isinstance(v, dict):
            v = to_dot(config=v, prefix=k, separator=separator)
        elif hasattr(v, '__call__') and function_to_name:
            v = {k: v.__name__ if hasattr(v, '__name__') else str(v)}
        else:
            v = {k: v}
        result.update(v)
    return result