【问题标题】:How to create the instance for my own type?如何为我自己的类型创建实例?
【发布时间】:2021-12-27 15:02:56
【问题描述】:

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

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

 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)

我被要求为扩展 Either 做 Functor、Applicative、Monad 实例,称为 MyEither

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

我的问题是:

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

2 推导必须写在实例中吗?

谁能给我一些建议?

【问题讨论】:

  • 要在某些MyEither a bx 上实现fmap f,请在x 中找到所有b 类型的值并将f 应用于它们,其余部分保持原样-- 每个 Functor 实例都以这种方式工作。
  • @chi 值x 是什么意思?
  • 首先调用它MyNothing 以防止任何混淆。

标签: haskell monads functor applicative algebraic-data-types


【解决方案1】:

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

对于Nothing,对其进行模式匹配并返回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 中 MaybeFunctorApplicativeMonad 实现。他们处理Nothing 的方式与您需要为您的数据类型执行的操作非常相似。

【讨论】:

    【解决方案2】:

    当你写下你的实例时,首先明确地写出类型:

    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) = ...。 (但请参阅本文底部的重要说明)

    下一个可能性是

        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 =  .... 
    

    当然,第二个选项在技术上也是可能的@ 在这里构造一个MyEither a c 值,因为我们在这里完全无法访问任何a 类型值......除了undefined

    现在只剩下一种可能性,对于 MyEither a b 类型的值,我们的 fmap 必须根据其类型签名(记住,fmap :: (b -&gt; c) -&gt; MyEither a b -&gt; MyEither a c)处理。而那个值是...

        fmap bc MyNothing = 
    

    同样,我们必须生成一个MyEither a c 类型的值作为结果。这次我们无法访问任何类型的值,既不是b,也不是a。所以除了return没有别的选择了……

                           .......
    

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

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


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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-14
      • 1970-01-01
      • 1970-01-01
      • 2017-02-20
      相关资源
      最近更新 更多