【问题标题】:How can I instantiate Functor for this data type?如何为这种数据类型实例化 Functor?
【发布时间】:2022-02-09 01:16:17
【问题描述】:

如何将以下数据类型实例化为 Functor?

data LiftItOut f a = LiftItOut (f a)
data Parappa f g a = DaWrappa (f a) (g a)
data IgnoreOne f g a b = IgnoringSomething (f a) (g b)
data Notorious g o a t = Notorious (g o) (g a) (g t)

声明本身不是很清楚,在右边成员的括号内,是函数应用程序(我从未见过,只有基本类型构造函数)?我是 haskell 的新手,我只是想了解基础知识。

【问题讨论】:

  • 如果fMaybe,那么f aMaybe a,所以f 有一种Type -> Type
  • 您是否尝试过使用打字孔? instance Functor (LiftItOut f) where fmap f (LiftItOut x) = _1 应该给你一个有用的信息。 (注意:除了填写该空白之外,您还必须进行更改;按照错误消息进行操作。)
  • @WillemVanOnsem 所以这意味着 f a 是经典(具体)类型?或者,可以说 f 是“显示”而 a 是 1 吗?
  • @Johnny: f a 是具体类型是的。
  • @WillemVanOnsem 假设我有LiftItOut value 没有更多的 f 和更多的 a ``` f1 a1 = f2 a2 ```,我怎么知道它的类型?抱歉,如果这些问题看起来很愚蠢,但我真的是 haskell 的新手并正在努力学习。

标签: haskell functor


【解决方案1】:

请编译器向您展示如何操作。使用命令行标志-ddump-deriv,启用DeriveFunctor语言扩展,并将deriving Functor放在每个类型定义的末尾,然后编译器将为每个类型打印Functor实例:

==================== Derived instances ====================
Derived class instances:
  instance GHC.Base.Functor g =>
           GHC.Base.Functor (Main.Notorious g o a) where
    GHC.Base.fmap f_aK1 (Main.Notorious a1_aK2 a2_aK3 a3_aK4)
      = Main.Notorious a1_aK2 a2_aK3 (GHC.Base.fmap f_aK1 a3_aK4)
    (GHC.Base.<$) z_aK5 (Main.Notorious a1_aK6 a2_aK7 a3_aK8)
      = Main.Notorious a1_aK6 a2_aK7 ((GHC.Base.<$) z_aK5 a3_aK8)
  
  instance forall k (f :: k -> *) (g :: * -> *) (a :: k).
           GHC.Base.Functor g =>
           GHC.Base.Functor (Main.IgnoreOne f g a) where
    GHC.Base.fmap f_aK9 (Main.IgnoringSomething a1_aKa a2_aKb)
      = Main.IgnoringSomething a1_aKa (GHC.Base.fmap f_aK9 a2_aKb)
    (GHC.Base.<$) z_aKc (Main.IgnoringSomething a1_aKd a2_aKe)
      = Main.IgnoringSomething a1_aKd ((GHC.Base.<$) z_aKc a2_aKe)
  
  instance (GHC.Base.Functor f, GHC.Base.Functor g) =>
           GHC.Base.Functor (Main.Parappa f g) where
    GHC.Base.fmap f_aKf (Main.DaWrappa a1_aKg a2_aKh)
      = Main.DaWrappa
          (GHC.Base.fmap f_aKf a1_aKg) (GHC.Base.fmap f_aKf a2_aKh)
    (GHC.Base.<$) z_aKi (Main.DaWrappa a1_aKj a2_aKk)
      = Main.DaWrappa
          ((GHC.Base.<$) z_aKi a1_aKj) ((GHC.Base.<$) z_aKi a2_aKk)
  
  instance GHC.Base.Functor f =>
           GHC.Base.Functor (Main.LiftItOut f) where
    GHC.Base.fmap f_aKl (Main.LiftItOut a1_aKm)
      = Main.LiftItOut (GHC.Base.fmap f_aKl a1_aKm)
    (GHC.Base.<$) z_aKn (Main.LiftItOut a1_aKo)
      = Main.LiftItOut ((GHC.Base.<$) z_aKn a1_aKo)

这看起来有点凌乱,但清理起来相当简单:

data LiftItOut f a = LiftItOut (f a)
instance Functor f => Functor (LiftItOut f) where
    fmap f (LiftItOut a) = LiftItOut (fmap f a)

data Parappa f g a = DaWrappa (f a) (g a)
instance (Functor f, Functor g) => Functor (Parappa f g) where
    fmap f (DaWrappa a1 a2) = DaWrappa (fmap f a1) (fmap f a2)

data IgnoreOne f g a b = IgnoringSomething (f a) (g b)
instance Functor g => Functor (IgnoreOne f g a) where
    fmap f (IgnoringSomething a1 a2) = IgnoringSomething a1 (fmap f a2)

data Notorious g o a t = Notorious (g o) (g a) (g t)
instance Functor g => Functor (Notorious g o a) where
    fmap f (Notorious a1 a2 a3) = Notorious a1 a2 (fmap f a3)

另外值得注意的是,您的LiftItOutApIdentityT 同构,您的ParappaProduct 同构。

【讨论】:

  • -dsuppress-module-prefixes 将非常有效地清理-ddump-deriv
  • 另外值得一提的是LiftItOut应该可能newtype
  • 顺便问一下,为什么要删除&lt;$ 定义?这些对这些类型很重要!
  • @dfeuer 它们不只是对性能重要,而不是对正确性重要吗?这似乎是一个新手问题,解释它们的额外复杂性似乎不是一个好主意。
  • 我想这是合理的。
【解决方案2】:

仿函数 f 是一个类型构造函数,它有一个关联函数 fmap,它从 (a -&gt; b) 类型的函数创建一个 (f a) -&gt; (f b) 类型的函数并应用它 "在里面”(括号是多余的,仅用于清晰/强调)

fmap :: (Functor f) => (  a  ->    b) 
                    -> (f a) -> (f b)       

-- i.e.           g ::    a  ->    b        -- from this
--           --------------------------
--           fmap g :: (f a) -> (f b)       -- we get this

(从 abg 中读取“fmap”从 f af b")。

换句话说,“函子”意味着它可以替代f in

fmap id (x :: f a)  =  x
(fmap g . fmap h)   =  fmap (g . h)

这样所涉及的表达式才有意义(即格式正确,即具有类型),而且,重要的是, 上述等式成立——它们实际上是两个 “函子法律”


你有

data  LiftItOut h a  =  MkLiftItOut    (h a)       -- "Mk..." for "Make..."
      -------------     -----------    ------
      new type,         data           type of the data constructor's
      defined here       constructor    one argument (one field)

这意味着h a 是一种事物,可以作为MkLiftItOut 的参数。比如Maybe Int(即h ~ Maybea ~ Int)、[(Float,String)](即h ~ []a ~ (Float,String))等

ha 是类型变量——意思是,它们可以被任何特定类型替换,这样整个语法表达式才有意义。

这些句法表达式包括MkLiftItOut x,它是LiftItOut h a 类型的事物,前提是xh a 类型的事物; LiftItOut h a 是一个类型; h a 是一种事物,可以作为 MkLiftItOut 的参数出现。因此我们可以在我们的程序中拥有

v1 = MkLiftItOut ([1,2,3]   :: []    Int   )  ::  LiftItOut  []    Int
v2 = MkLiftItOut ((Just "") :: Maybe String)  ::  LiftItOut  Maybe String
v3 = MkLiftItOut (Nothing   :: Maybe ()    )  ::  LiftItOut  Maybe ()
.....

等等。然后我们有

ghci> :i Functor
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a
..........

这意味着Functor f =&gt; (f a)是一种变量可以引用的事物类型,例如

                 -- f        a
v4  = Just 4     :: Maybe   Int
v41 =      4     ::         Int

v5  = [4.4, 5.5] :: []      Float
v51 =  4.4       ::         Float
v52 =       5.5  ::         Float

v6  = (1,"a")    :: ((,) Int)  String  -- or simpler, `(Int, String)`
v61 =    "a"     ::            String

v7  = (\x -> 7)  :: ((->) Int) Int     -- or simpler, `Int -> Int`

这里a是一个事物的类型,f a是一个事物的类型,f是一个类型,当给定一个事物的类型时,它就变成了一个事物的类型;等等。没有任何东西可以被单独具有 f 类型的变量引用。

以上所有fs 都是Functor 类型类的实例。这意味着在图书馆的某处有定义

instance Functor Maybe   where ....
instance Functor []      where ....
instance Functor ((,) a)  where ....
instance Functor ((->) r) where ....

请注意,我们总是有 faf 尤其可以由多个组成部分组成,但 a 始终是某个一个类型。

因此在这种情况下我们必须有

instance Functor (LiftItOut h) where ....

(...为什么? 一定要说服自己;看看上述所有陈述如何适用和正确)

那么实际定义一定是

   -- fmap :: (a -> b) -> f           a -> f           b
   -- fmap :: (a -> b) -> LiftItOut h a -> LiftItOut h b
   fmap       g       (MkLiftItOut  x )  =  (MkLiftItOut  y )
     where
     y = ....

特别是,我们将有

    --     g :: a -> b     --   x :: (h a)     --   y :: (h b)

我们甚至不知道h 是什么。

我们如何解决这个问题?当我们对 hab 都不了解的情况下,我们如何从 h a 类型的事物构造 h b 类型的事物?

我们不能。

但是如果我们知道h 也是Functor 呢?

instance (Functor h) => Functor (LiftItOut h) where
   -- fmap :: (a -> b) -> (f           a) -> (f           b)
   -- fmap :: (a -> b) -> (LiftItOut h a) -> (LiftItOut h b)
   fmap       g       (MkLiftItOut  x )  =  (MkLiftItOut  y )
     where
     -- fmap :: (a -> b) -> (h a) -> (h b)
     y = ....

希望你能完成这件事。并且也在你的问题中做其他类型。如果没有,请针对 one 类型发布一个新问题,您可能会遇到任何其他问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多