为具有两个相同类型的自定义数据类型实现应用仿函数 类

Implementing applicative functor for custom data type with two identical type classes

我试图让 Twice 成为 applicative 的成员,但我在仿函数实现中得到 "Conflicting definitions for `a'":我也不确定如何正确实现 <*> :/

data Twice a = Twice a a deriving (Show)

instance Functor Twice where
    fmap f (Twice a a) = Twice (f a) (f a)

instance Applicative Twice where
    pure x = (Twice x) x
    Twice f f <*> Twice x x = Twice ((f x) (f x))

以下是问题及其更正:

instance Functor Twice where
    -- fmap f (Twice a a) = Twice (f a) (f a)
    --               ^^^ you define `a` twice here. Replace this with:
    fmap f (Twice a b) = Twice (f a) (f b)

instance Applicative Twice where
    pure x = Twice x x -- You don't need brackets here so I removed them.
    -- Twice f f <*> Twice x x = Twice ((f x) (f x))
    -- ^^^^^^^^^     ^^^^^^^^^         ^^^^^^^^^^^^^
    -- You define f and x twice.
    -- You are also misusing brackets - this is not how to apply functions.
    -- A corrected version:
    Twice f1 f2 <*> Twice x1 x2 = Twice (f1 x1) (f2 x2)

首先,如果您不给单独的参数单独的名称,这导致错误。假设我写 f a a = a + 6,然后让你评估 f 1 2.

其次,您误解了 Haskell 函数的应用方式。让我澄清一下:在大多数语言中,你写 f(a,b,c),但在 Haskell 中,你写 f a b c。如果您写 f (a b c),这将读作 f(a(b,c))

我猜这些错误更多的是语法错误而不是语义错误。在您的定义中:

Twice <b>f</b> <b>f</b> <*> Twice <b>x</b> <b>x</b> = Twice ((f x) (f x))

Functor定义相同:

fmap f (Twice <b>a</b> <b>a</b>) = Twice (f a) (f a)

你在头上写了两个f和两个x。 Haskell 不允许这样做(Prolog 允许,但即便如此,它也可能不是您的意思)。

此外,您在正文中写道:

((f x) (f x))

这意味着您将使用 f x 函数和 f x 参数进行函数调用,这可能又不是您想要的。

我们可以在语法上将其修复为:

instance Functor Twice where
    fmap f (Twice a <b>b</b>) = Twice (f a) (f <b>b</b>)

instance Applicative Twice where
    pure x = Twice x x
    Twice <b>f</b> <b>g</b> <*> Twice <b>x</b> <b>y</b> = Twice <b>(f x) (g y)</b>

请注意,您仍然需要通过考虑为这些类型类记录的约束来证明这是一个有效的 FunctorApplicative 实例。

混淆源于对数据和类型使用相同的名称;一种常见的、被接受的、甚至是推荐的做法,适用于除非常新手 Haskell 以外的所有人。

目前,您可以使用不同的名称,以减少学习和习惯 Haskell 的数据类型声明时的认知负担:

data Twice a = MkTwice a a deriving (Show)         -- 'Mk' is for "make"

-- Twice a                         -- a type
-- MkTwice :: a -> a -> Twice a    -- data constructor

instance Functor Twice where
    -- fmap (f :: a -> b) :: Twice a -> Twice b
    fmap f (MkTwice x y) = MkTwice (f x) (f y)     -- transform both fields

instance Applicative Twice where
    pure x = MkTwice x x                 -- create two fields from one value
    MkTwice f g <*> MkTwice x y = MkTwice (f x) (g y)  -- combine both fields

Twice a 是一个类型,a 是一个类型变量。

MkTwice 是该类型的数据构造函数。它从两个 a 类型的值(比如 x :: ay :: a)生成 Twice a 类型的值。值出现在 Haskell 定义中 = 的右侧(大致)。

它也用于 Twice a 类型值的模式。模式出现在 Haskell 定义中 = 的左侧。在 Haskell 的模式中不能有 没有 重复的模式变量。每个模式变量代表一些值。

每个类型变量代表一种类型。

我敢打赌,如果您对类型和数据构造函数使用不同的名称,您就不会犯这些错误。

它可以使用基础 4.17.0.0 派生,使用 Generically1 来自 GHC.Generics:

{-# Language DerivingStrategies #-}
{-# Language DerivingVia        #-}

import GHC.Generics (Generic1, Generically1(..))

data Twice a = Twice a a
  deriving 
  stock (Show, Generic1)

  deriving (Functor, Applicative)
  via Generically1 Twice