函数类型的 Applicative 和 Monad 实例?

Applicative and Monad instances for a function type?

我有一个类似于下面的 Blah 的数据类型,但 由于该类型的一个怪癖,我无法自动派生 Functor、Applicative 和 Monad。所以我必须手动完成,但我不确定如何。我试图从 ((->) a) 的实例中获取灵感,但我不太明白 Monad 实例。

newtype Blah a = Blah (String -> a) -- deriving (Functor, Applicative, Monad)

-- this seems right
instance Functor Blah where
  fmap f (Blah g) = Blah (f .  g)

instance Applicative Blah where
  pure = Blah . const
  -- This is right, right?
  (<*>) (Blah f) (Blah g) = Blah $ \x -> f x (g x)

instance Monad Blah where
  return = pure

  -- I'm not having any luck here.
  (>>=) a b = Blah $ \c -> _

编辑:有人将此标记为与另一个重复,但我不知道从哪里可以得到答案。新型包装器使这变得困难。在我写这个问题之前,我在 (->) a 的基础中查找了 Monad 实例,但是这里答案中的体操是我需要的。

您可以 derive 这些实例。您只需要打开 GeneralizedNewtypeDeriving 标志,这使 GHC 可以简单地重用包装类型的实例。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Blah a = Blah (String -> a) deriving (Functor, Applicative, Monad)

怎么样

Blah f >>= g = Blah $ \s ->
    let Blah h = g $ f s in h s

以下是您可以使用类型化孔自行推导的方法。从你的代码开始,改名了一下:

instance Monad Blah where
  return = pure
  f >>= g = Blah $ \s -> _

您会收到这样的消息:

Found hole ‘_’ with type: b
Relevant bindings include
  s :: String
  g :: a -> Blah b
  f :: Blah a

因此我们需要生成一个 b,给定一个 String、一个 Blah a 和一个 a -> Blah b。好吧,我们已经知道如何通过模式匹配和应用 Blah:

中的函数从 Blah aString 生成 a
Blah f >>= g = Blah $ \s -> let h = f s in _
------                      -----------

现在我们得到:

Found hole ‘_’ with type: b
Relevant bindings include
  h :: a
  s :: String
  g :: a -> Blah b
  f :: String -> a

所以我们有一个 a,我们可以把它给 g 来得到一个 Blah b,然后模式匹配给我们一个 String -> b:

Blah f >>= g = Blah $ \s -> let Blah h = g (f s) in _
                                ----------------

现在我们得到:

Found hole ‘_’ with type: b
Relevant bindings include
  h :: String -> b
  s :: String
  g :: a -> Blah b
  f :: String -> a

所以我们需要一个b,我们有一个String和一个String -> b。这很简单:

Blah f >>= g = Blah $ \s -> let Blah h = g (f s) in h s
                                                    ---

好了,正确的实现,以类型为指导。如果你定义一个辅助函数到 “运行” a Blah:

你可能也会发现它更清楚
newtype Blah a = Blah { runBlah :: String -> a }
-- or:
runBlah :: Blah a -> String -> a
runBlah (Blah f) = f

instance Monad Blah where
  f >>= g = Blah $ \s -> runBlah (g (runBlah f s)) s