【问题标题】:Is this a Functor or Monad?这是 Functor 还是 Monad?
【发布时间】:2014-05-27 17:52:48
【问题描述】:

我在 C# 中实现了我自己的 Promise 结构,并想在 Haskell 中测试这个概念,所以经过一些严格的大脑锻炼(对此仍然很新)我制作了

data Promise f a =  PendingPromise f | ResolvedPromise a | BrokenPromise deriving( Show )

class Future p where
        later :: (b -> c) -> p (a -> b) b -> p (a -> c) c
        resolve :: p (a -> b) a -> a -> p (a -> b) b

instance Future Promise where
        later g (PendingPromise f) = PendingPromise (g . f)
        later g (ResolvedPromise a) = ResolvedPromise (g a)
        resolve (PendingPromise f) a = ResolvedPromise (f a)

弄清楚如何编写此数据类型Promise f a 真是令人头疼。

无论如何,later 方法似乎是某种 Applicative Functor 和 Promises 应该是 Monads。我是否可以将 Promise 设为某个 class 的实例并获得此功能,而不是实现我自己的 Future 类?


编辑 感谢@bheklilr,later 函数被证明是 fmap,只是对数据类型进行了一点重新定义

data Promise a b =  PendingPromise (a -> b) | ResolvedPromise b | BrokenPromise

instance Functor (Promise c) where
    fmap f (PendingPromise g) = PendingPromise (f . g)
    fmap f (ResolvedPromise a) = ResolvedPromise (f a)
    fmap f BrokenPromise = BrokenPromise

知道 (Promise a) 是 Functor,就更容易理解为什么是 Monad。

【问题讨论】:

  • 每个 Monad 都是 Applicative。

标签: haskell


【解决方案1】:

是的,它是一个单子。看到这一点的最简单方法是观察 Promise f a ≅ Maybe (Either f a),因此它也与具有已被证明的标准 monad 实例的转换器等效项同构。

type Promise' f = ErrorT f Maybe

promise2Trafo :: Promise f a -> Promise' f a
promise2Trafo (PendingPromise f) = ErrorT . Just $ Left f
promise2Trafo (ResolvedPromise a) = ErrorT . Just $ Right a
promise2Trafo BrokenPromise = ErrorT Nothing

trafo2Promise :: Promise' f a -> Promise f a
trafo2Promise = ... -- straightforward inverse of `promise2Trafo`

instance Applicative Promise where
  pure = trafo2Promise . pure
  fp <*> xp = trafo2Promise $ promise2Trafo fp <*> promise2Trafo xp

等等。

【讨论】:

【解决方案2】:

您要做的是尝试实现Functor 的实例并检查它是否符合Functor Laws。从实例开始:

instance Functor (Promise f) where
    fmap f (PendingPromise g) = PendingPromise g
    fmap f (ResolvedPromise a) = ResolvedPromise (f a)
    fmap f BrokenPromise = BrokenPromise

这是否符合函子定律?由于PendingPromiseBrokenPromise 的情况始终是身份,为简洁起见,我将它们排除在外:

-- fmap id = id
fmap id (ResolvedPromise a) = ResolvedPromise (id a) = ResolvedPromise a

-- fmap (f . g) = fmap f . fmap g
fmap (f . g) (ResolvedPromise a) = ResolvedPromise (f (g a))
fmap f (fmap g (ResolvedPromise a)) = fmap f (ResolvedPromise (g a)) = ResolvedPromise (f (g a))

所以是的,它确实符合函子定律。

接下来,看看你能不能写出ApplicativeMonad的实例,并证明它们符合各自的规律。如果您可以编写一个实例,那么您的数据类型就是Monad,与Future 类无关。

【讨论】:

  • 这一行fmap f (PendingPromise g) = PendingPromise g 应该是这样的fmap f (PendingPromise g) = PendingPromise (f. g)。有问题吗?
  • @CristianGarcia 不,不应该是这样的。请注意,Promise f 是函子,而不是 Promise。应用fmap时,会将类型从Promise f a更改为Promise f b,具体为fmap :: (a -&gt; b) -&gt; Promise f a -&gt; Promise f b。由于f 没有改变,所以fmap 不会改变它。
  • 哇,我去看看!
  • 这实际上看起来很像你正在尝试实现相当于失败的函数组合,所以由于函数是单子,并且拥有 BrokenPromise 本质上就像拥有也许单子,我的直觉是你的类型是一个单子。但是,如果仍然证明法律,否则您将不知道它是否表现良好。
  • 证明法律很难。构造Promise fEither (Maybe f)之间的同构,并使用后者的Monad实例!
【解决方案3】:

这是我想出的最终代码,如果它对某人有用的话。 Promise 是 Functor 类的一个实例,这就是问题所在,但也实现了 Future 类,该类可以解决 Promise,可能会获取它的值,并做出新的 Promise。

data Promise a b =  Pending (a -> b) | Resolved b | Broken

class Future p where
        resolve :: p a b -> a -> p a b
        getValue :: p a b -> Maybe b
        makePromise :: p a a

instance Future Promise where
        resolve (Pending f) a = Resolved (f a)
        resolve (Resolved a) _ = Resolved a
        resolve Broken _ = Broken

        getValue (Resolved a) = Just a
        getValue (Pending _) = Nothing
        getValue Broken = Nothing

        makePromise = Pending id

instance Functor (Promise a) where
        fmap f (Pending g) = Pending (f . g)
        fmap f (Resolved a) = Resolved (f a)
        fmap f Broken = Broken

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-06
    • 2016-12-24
    • 2012-01-29
    • 2011-11-05
    • 2020-01-31
    • 2012-01-17
    相关资源
    最近更新 更多