如何为我自己的类型创建实例?

How to create the instance for my own type?

我是 Haskell 的新手,正在学习 Functor、Applicative 和 Monad。

我检查了来自黑客的 Either 类型的 Functor 实例是:

 data  Either a b  =  Left a | Right b

 instance Functor (Either a) where
    fmap _ (Left x) = Left x
    fmap f (Right y) = Right (f y)

我被要求为名为 MyEither:

的扩展 Either 执行 Functor、Applicative、Monad 实例
data MyEither a b = MyLeft a | MyRight b | Nothing
    deriving (Eq, Show)

我的问题是:

1 如何在我的实例中扩展 Nothing

2派生是否一定要写在实例中?

谁能给我一些提示?

实现实例时无需编写 derivingderiving 用于在定义新类型时让编译器为您派生实例。

对于Nothing,模式匹配就可以了,return对于Nothing的执行都是FAM。您无能为力。

这是一个类似的例子,可能会对您有所帮助。

data Nope a = NopeDataJpg deriving (Eq, Show)

instance Functor Nope where
  fmap _ _ = NopeDataJpg
  
instance Applicative Nope where
  pure x = NopeDataJpg
  _ <*> _ = NopeDataJpg

instance Monad Nope where
  return = pure
  _ >>= _ = NopeDataJpg

另外,这里是 GHC 中 MaybeFunctor, Applicative, and Monad 实现。他们处理 Nothing 的方式与您需要对数据类型执行的操作非常相似。

写下实例时,首先要明确写出类型:

data MyEither a b = MyLeft a | MyRight b | MyNothing
    deriving (Eq, Show)

instance Functor (MyEither a) where
    -- fmap :: Functor  f => (b -> c) -> f b -> f c
    -- fmap :: (b -> c) -> MyEither a b -> MyEither a c

(我将在这里使用 MyNothing 以防止与 MaybeNothing 混淆)。

现在我们准备根据数据类型定义枚举所有的可能性:

    fmap bc (MyLeft aValue) = result   where
    --   bc                ::            b -> c
    --       MyLeft aValue :: MyEither a b
    --   result            :: MyEither a      c
    .....

这里我们必须产生一个MyEither a c类型的值作为结果。函数 bc 在这里没有用,因为我们只能访问类型 a:

的值 aValue :: a
             MyLeft aValue :: MyEither a b
           --------------------------------
                    aValue ::          a

因此我们这里有两种可能:

  1. result = MyLeft aValue 将构造一个新值,这次的类型 MyEither a c 由类型签名确定;或
  2. result = ....

填满点,在1和2之间做出选择,完成本子句的定义,fmap bc (MyLeft aValue) = ...。 (但请参阅此 post 底部的重要注释)

下一种可能是

    fmap bc (MyRight bValue) = ...

这次我们确实可以访问 b 类型的值,因此函数 bc 可以派上用场,因为

          bValue :: b
       bc        :: b -> c
     -------------------------
       bc bValue ::      c

                cValue ::            c
       --------------------------------
        myRight cValue :: MyEither a c

因此这个子句的自然定义是

    fmap bc (MyRight bValue) = MyRight cValue
             where
             cValue =  .... 

当然,与第一个条款中相同的第二个选项在技术上也是可行的。(再次,请参阅此 post 底部的重要说明) 当然不可能的是在这里使用 MyLeft 构造一个 MyEither a c 值,因为我们在这里绝对无法访问任何 a 类型的值.... 除了 undefined.

现在只剩下一种可能性了,根据类型签名(记住,fmap :: (b -> c) -> MyEither a b -> MyEither a c),我们的 fmap 必须处理 MyEither a b 类型的值。该值是 ...

    fmap bc MyNothing = 

同样,我们必须生成一个 MyEither a c 类型的值作为结果。这次我们无法访问 ba 类型的任何值。所以除了 return ...

别无选择
                       .......

(当然没有使用 undefined)。请完成定义。

希望您现在能够根据需要完成其他定义。


重要更新:上面提到的那些选择实际上根本就没有选择。如果我们所关心的只是做出适合该类型的定义,那么这些可能性确实存在。但是,Functor 等实例也必须合法。例如函子定律是

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

并且在每种情况下,只有一种可能性不会违反法律。特别是,fmap id (MyLeft _) = MyNothing 违反了第一个 Functor 定律。