为类型同义词编写函子实例并感到困惑

New*_*ird 2 haskell

我正在尝试实现一个通用的类似环形的东西,并将其应用于 Paul Hudak 在《哈斯克尔音乐学院》一书中描述的音乐数据结构。当然,有很多半群/幺半群的恶作剧被省略,但我有以下相关代码:

\n
newtype Duo     a b     = Duo     {duo1     :: a} -- A ring-like structure\n\ndata Song a =\n       Primitive a\n     | Song a :+: Song a             -- Composing Music Sequentially\n     | Song a :=: Song a deriving Eq -- Composing Music Concurrently (in parallel)\n\ninstance Functor Song where\n     fmap f (x :+: y)     = fmap f x :+: fmap f y\n     fmap f (x :=: y)     = fmap f x :=: fmap f y\n     fmap f (Primitive x) = Primitive $ f x\n\nnewtype Concurrent a = Concurrent {fromConcurrent :: Song a} deriving (Show)\nnewtype Sequential a = Sequential {fromSequential :: Song a} deriving (Show)\n\ntype Music a = Duo (Maybe (Concurrent a)) (Maybe (Sequential a))\n
Run Code Online (Sandbox Code Playgroud)\n

我正在尝试编写一个用于音乐的 Functor 实例,因为 Duo 没有 Functor,我认为这不会成为问题。

\n

我写了以下实现:

\n
instance Functor Music where\n     fmap :: (a -> b) -> Music a -> Music b\n     fmap f = Duo . fmap (fmap f . fromConcurrent) . duo1\n
Run Code Online (Sandbox Code Playgroud)\n

但我收到以下错误:

\n
\n
\xe2\x80\xa2 The type synonym \xe2\x80\x98Music\xe2\x80\x99 should have 1 argument, but has been given none\n\xe2\x80\xa2 In the instance declaration for \xe2\x80\x98Functor Music\xe2\x80\x99\n| 167 | instance Functor Music where\n|          ^^^^^^^^^^^^^\n
Run Code Online (Sandbox Code Playgroud)\n
\n

也许问题在于我本质上只是为 Duo 的一个子集编写一个 Functor,也许这只有在我使音乐成为一种新类型而不仅仅是类型同义词时才有效。我真的希望避免这种情况,因为这段代码中的包装器数量已经非常多了,我真的不想再添加另一个。也许你们都可以想出一种方法来为 Duo 实现一个有意义的函子,并使这一切都能发挥作用?

\n

我真的不明白的一件事是为什么它让我举一个例子来展示:

\n
instance (Show a) => Show (Music a) where\n     show (Duo Nothing)               = "Silence"\n     show (Duo (Just (Concurrent x))) = show x\n
Run Code Online (Sandbox Code Playgroud)\n

Dan*_*ner 5

不幸的是,Haskell 中不允许这种类型级别的抽象。但我认为您可能在数据类型设计中的某个地方犯了某种概念错误。扩展类型,因为Duo只有一个字段,我们将有:

Music a ~= Duo (Maybe (Concurrent a)) (Maybe (Sequential a))
        ~= Maybe (Concurrent a)
        ~= Maybe (Song a)
Run Code Online (Sandbox Code Playgroud)

这真的是你的意图吗?

万一发生这种情况,我想我会很想去掉中间商。

data Music a = Silence | Sound (Song a)
instance Functor Music where
    fmap f Silence = Silence
    fmap f (Sound notes) = Sound (fmap f notes)
Run Code Online (Sandbox Code Playgroud)

如果这是预期的行为,但你必须保留中间人,newtype这就是前进的方向。

如果这可能不是您想要的Music行为方式,我们可能会给您更多/更好的建议,但我们需要更多地了解您的意图Duo

关于您的编辑/评论Show:在您的Show实例中,您包含一个类型参数Music。该实例的问题正是您包含参数的Functor事实。在实例中,由于参数可用,编译器可以简单地扩展类型别名,因此该行Show

instance Show a => Show (Music a)
Run Code Online (Sandbox Code Playgroud)

编译器关心的各方面都与该行相同

instance Show a => Show (Duo (Maybe (Concurrent a)) (Maybe (Sequential a)))
Run Code Online (Sandbox Code Playgroud)

但是,在您的Functor实例中,编译器无法扩展类型别名,因为您尚未提供类型参数。你可以想象在 Haskell 中添加类型级别的 lambda,这样你的instance Functor Music意思就可以是这样的

instance Functor (\a -> Duo (Maybe (Concurrent a)) (Maybe (Sequential a)))
Run Code Online (Sandbox Code Playgroud)

但这有一些严重的问题。添加它的天真方式导致您需要在类型检查期间进行高阶统一,这是不可判定的。

  • @New_Caird 是的,问题是专门删除参数“a”。Show 实例包含参数,因此编译器可以简单地扩展类型别名;但您提出的“Functor”实例没有,所以它不能。 (2认同)