Haskell-具有函数构造函数的数据类型上的自定义函子实例

Haskell-具有函数构造函数的数据类型上的自定义函子实例,haskell,functor,Haskell,Functor,我无法为无法更改的自定义数据类型编写自己的functor实例。数据类型定义为: data Foo a = Baz String (Qux -> Foo a) | Bar a data Qux = None | Quux String 我的问题是为Foo类型编写函子。具体来说,我不知道如何正确地将我的函子函数f应用于Foo中的函数。我想在构造函数中调用这个函数,但是因为我没有任何可用的Qux,所以我被卡住了 instance Functor Foo where fmap f (Ba

我无法为无法更改的自定义数据类型编写自己的functor实例。数据类型定义为:

data Foo a = Baz String (Qux -> Foo a) | Bar a
data Qux = None | Quux String
我的问题是为Foo类型编写函子。具体来说,我不知道如何正确地将我的函子函数f应用于Foo中的函数。我想在构造函数中调用这个函数,但是因为我没有任何可用的Qux,所以我被卡住了

instance Functor Foo where
    fmap f (Bar a) = Bar (f a)
    fmap f (Baz s ???) = Baz s (???) -- What goes here?

    -- Clearly, something like this doesn't work
    -- fmap f (Baz s g) = Baz s (f g) 

    -- I've also tried something like this, but I'm not sure where to go from there
    -- fmap f (Baz s (None   -> Bar b)) = Baz s (f b) ???
    -- fmap f (Baz s (Quux x -> Bar b)) = Baz s ???

让我们从完成这个等式的左边开始。我们可以编写g来将函数绑定到变量

fmap f Baz s g=Baz s

然后,我们需要用另一个函数填充问号,该函数接受一个Qux并返回一个foob

fmap f Baz s g=Baz s\q->

对于q,我们只能做一件事,那就是将g应用于它

fmap f Baz s g=Baz s\q->g q

然而,这给了我们一个Foo a,但我们需要一个Foo b!我们没有这样做的功能。然而,我们有f,它取a,返回b。如果有办法把a->b变成Foo a->Foo b。。。哦,等等,有,它叫fmap

fmap f Baz s g=Baz s\q->fmap f g q

如果您想用无点表示法编写函数,可以这样做


fmap f Baz s g=Baz s fmap f。g

让我们从完成这个等式的左侧开始。我们可以编写g来将函数绑定到变量

fmap f Baz s g=Baz s

然后,我们需要用另一个函数填充问号,该函数接受一个Qux并返回一个foob

fmap f Baz s g=Baz s\q->

对于q,我们只能做一件事,那就是将g应用于它

fmap f Baz s g=Baz s\q->g q

然而,这给了我们一个Foo a,但我们需要一个Foo b!我们没有这样做的功能。然而,我们有f,它取a,返回b。如果有办法把a->b变成Foo a->Foo b。。。哦,等等,有,它叫fmap

fmap f Baz s g=Baz s\q->fmap f g q

如果您想用无点表示法编写函数,可以这样做

fmap f Baz s g=Baz s fmap f。g

遵循以下类型:

fmap f (Baz s g) = GOAL
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- GOAL :: Foo b
所以我们正在寻找一个球线。我们可以用吧台或巴兹做一个。fmap应保留结构,因此应为Baz。Baz的String参数可能不应该更改,这只是引起问题的第二个参数

fmap f (Baz s g) = Baz s GOAL
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- GOAL :: Qux -> Foo b
现在我们要做一首曲子->福b。如果你需要做一个函数,lambda是必不可少的工具,还有其他工具,但是lambda是最重要的。所以做一个lambda:

fmap f (Baz s g) = Baz s (\x -> GOAL)
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- x    :: Qux          <-- NEW
   -- GOAL :: Foo b
请注意,我们现在有一个x::Qux可以使用。使用g和x我们可以得到一个Foo a,然后使用fmap f递归1我们可以得到所需的Foo b

请注意我是如何一次只填入一个表达式,用一个目标替换未知的参数,然后再考虑一下我在范围内的变量以及它们的类型。继续这样做,直到目标明确为止

1递归类型通常具有相应的递归fmap定义。递归在fmap中发生的位置与类型的递归方式完全对应。

遵循类型:

fmap f (Baz s g) = GOAL
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- GOAL :: Foo b
所以我们正在寻找一个球线。我们可以用吧台或巴兹做一个。fmap应保留结构,因此应为Baz。Baz的String参数可能不应该更改,这只是引起问题的第二个参数

fmap f (Baz s g) = Baz s GOAL
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- GOAL :: Qux -> Foo b
现在我们要做一首曲子->福b。如果你需要做一个函数,lambda是必不可少的工具,还有其他工具,但是lambda是最重要的。所以做一个lambda:

fmap f (Baz s g) = Baz s (\x -> GOAL)
   -- f    :: a -> b
   -- s    :: String
   -- g    :: Qux -> Foo a
   -- x    :: Qux          <-- NEW
   -- GOAL :: Foo b
请注意,我们现在有一个x::Qux可以使用。使用g和x我们可以得到一个Foo a,然后使用fmap f递归1我们可以得到所需的Foo b

请注意我是如何一次只填入一个表达式,用一个目标替换未知的参数,然后再考虑一下我在范围内的变量以及它们的类型。继续这样做,直到目标明确为止


1递归类型通常具有相应的递归fmap定义。在fmap中递归发生的位置与类型的递归方式完全对应。

第一部分很简单:Baz是两种类型的产物,因此您只需要在Baz s g上进行模式匹配,其中g::Qux->Foo a。如何处理g是一个棘手的部分:在Haskell中,考虑这一点的方法通常是遵循类型。你有一个函数f::a->b,一个我称之为g的函数,类型为Qux->fooa,你想要一个类型为Qux->foob的函数。我敢肯定,只有一种方法可以利用这里的数据来实现这一点——要看到这一点,不要害怕使用递归;第一部分很简单:Baz是两种类型的产品,所以您只需要在Baz上进行模式匹配,其中g::Qux->fooa。如何处理g是一个棘手的部分:A
在Haskell中,思考这个问题的方法就是遵循类型。你有一个函数f::a->b,一个我称之为g的函数,类型为Qux->fooa,你想要一个类型为Qux->foob的函数。我敢肯定,只有一种方法可以利用这里的数据来实现这一点——要看到这一点,不要害怕使用递归;非常感谢!这就是我遇到的问题。我忘了我可以用兰姆达斯那样的。非常感谢!这就是我遇到的问题。我忘了我可以像那样使用lambdas。