如何在Python中编写基本的逆变函数定义?

时间:2017-04-22 22:27:27

标签: haskell functor python-3.6 contravariance

我一直试图通过将它们翻译成Python来理解Haskell中的一些抽象概念,但我似乎无法弄清楚如何编写一个满足定义逆变函子的公理的类。

我认为以下课程在非可迭代的基础上捕捉基本仿函数的概念。类型。

from typing import Any, Iterable
from types import FunctionType as Function
from abc import abstractmethod


class _FunctorBase:

    value: Any = None

    def __call__(self, value: Any) -> '_FunctorBase':
        """ Make the functor type callable """
        self.value = value
        return self

    def __str__(self) -> str:
        return f'{self.__class__.__name__}({self.value})'

    def __repr__(self) -> str:
        return self.__str__()

    def __eq__(self, other: '_FunctorBase') -> bool:
        return self.value == other.value

    def __ne__(self, other: '_FunctorBase') -> bool:
        return not self.__eq__(other)

    @abstractmethod
    def fmap(self, f: Function) -> '_FunctorBase':
        raise NotImplementedError


class FunctorWrapper(_FunctorBase):
    """ Just a basic wrapping functor for a non-iterable value """

    def fmap(self, f: Function) -> 'FunctorWrapper':
        return FunctorWrapper()(f(self.value))

我可以证明这些类型满足以下公理(至少对于特定的lambda函数),如Hackage所示,

fmap id  ==  id
fmap (f . g)  ==  fmap f . fmap g

如下:

def id(x: Any) -> Any: return x


def test_functor() -> None:
    wrap = FunctorWrapper()(3)

    print(wrap.fmap(id))
    print(wrap.fmap(lambda x: x + 1))
    print(wrap.fmap(lambda x: x + 1).fmap(lambda x: x + 1))

    assert wrap.fmap(lambda x: x + 1).fmap(lambda y: y * 2) == \
           wrap.fmap(lambda z: (z + 1) * 2), \
        'Functor does not satisfy composition axiom'

    assert wrap.fmap(id) == wrap, 'Functor does not satisfy identity axiom'

    print('** Functor passes all axioms')

test_functor()

# FunctorWrapper(3)
# FunctorWrapper(4)
# FunctorWrapper(5)
# ** Functor passes all axioms

或者,如果您更喜欢可以映射到的类型。以更明确的方式:

class FunctorIterableList(_FunctorBase):

    def fmap(self, f: Function) -> 'FunctorIterable':
        return FunctorIterableList()(list(map(f, self.value)))

# FunctorIterableList([1, 2, 3, 4])
# FunctorIterableList([2, 3, 4, 5])
# FunctorIterableList([3, 4, 5, 6])
# ** Functor passes all axioms

然而,逆变函子必须满足组合的逆转,

contramap f . contramap g = contramap (g . f)

我不知道如何以这种方式反转Python中的函数组合;我一直在想解决方案可能是在内部将第一个动作存储在类定义中,然后,在第二个调用时,在第一个之前应用第二个动作?必须有一种方法来满足这一要求而不在内部存储这种信息。

0 个答案:

没有答案