3

假设我们有一个 typeclass class (A a, B a) => C a where。使用newtype将允许我们克隆数据类型,然后通过GeneralizedNewtypeDeriving语言扩展自动派生实例(请参阅如何编写可派生类?处理具有相同内部表示和最小样板的多种类型?)。

问题:是否有可能让 ghc 自动派生Aand C,但使用我们自己指定的Bin deriving实现C

例如,以下代码(其中A= PlanetB= LivesC= Description)无法按预期工作:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Main (main) where

data Cat = Cat String
newtype Dolphin = Dolphin Cat deriving (Planet)

------------------------------------------------

class Planet a where
  planet :: a -> String

class Lives a where
  lives :: a -> String

class (Planet a, Lives a) => Description a where
  description :: a -> String

------------------------------------------------

instance Planet Cat where
  planet _ = "lives on planet earth,"

instance Lives Cat where
  lives _ = "lives on land"

instance Description Cat where
  description a = (planet a) ++ (lives a)

------------------------------------------------

instance Lives Dolphin where
  lives _ = "lives in the sea"

--want the following derivation to use the instance of 
--"Lives" for "Dolphin" above
deriving instance Description Dolphin

------------------------------------------------

main = do
  print $ description (Cat "test")
  -- > "lives on planet earth,lives on land"
  -- OK
  print $ description (Dolphin (Cat "test"))
  -- > "lives on planet earth,lives on land"
  -- NOT OK. Want "lives on planet earth,lives in the sea"

我期待/想要的DolphinLivesDescription.

显然,以下程序可以工作,但它需要一个显式实例化Descriptionfor Dolphin

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Main (main) where

data Cat = Cat String
newtype Dolphin = Dolphin Cat deriving (Planet)

------------------------------------------------

class Planet a where
  planet :: a -> String

class Lives a where
  lives :: a -> String

class (Planet a, Lives a) => Description a where
  description :: a -> String

------------------------------------------------

instance Planet Cat where
  planet _ = "lives on planet earth,"

instance Lives Cat where
  lives _ = "lives on land"

instance Description Cat where
  description a = (planet a) ++ (lives a)

------------------------------------------------

instance Lives Dolphin where
  lives _ = "lives in the sea"

instance Description Dolphin where
  description a = (planet a) ++ (lives a)

------------------------------------------------

main = do
  print $ description (Cat "test")
  -- > "lives on planet earth,lives on land"
  --[OK]
  print $ description (Dolphin (Cat "test"))
  -- > "lives on planet earth,lives in the sea"
  --[OK]

ps 令人费解的是,如果(在第一个程序中)我没有声明:

instance Lives Dolphin where
  lives _ = "lives in the sea"

然后 ghc 抱怨:

Main.hs:36:1:
    No instance for (Lives Dolphin)
      arising from the superclasses of an instance declaration
    In the instance declaration for ‘Description Dolphin’

如果 ghc没有在for的(自动)推导中使用它,instance Lives Dolphin where它会抱怨缺少,这似乎很奇怪。DescriptionDolphin

4

2 回答 2

2

考虑以下:

newtype ProcessID = PID Int deriving Eq

这样做是编写一个看起来像的实例

instance Eq PID where
  (PID x) == (PID y)    =    x == y

换句话说,当您调用==a时PID,它会将其展开为普通Int的 ,然后==在其上执行。

我想这deriving instance Description Dolphin是完全一样的;将 a展开Dolphine为 a Cat,然后在其description上调用该方法。这根本不是你想要的!

问题:如果定义description总是相同的,为什么它需要是一个类?为什么不能只定义一个常规函数来执行此操作?

(或者这是您要解决的一些更复杂问题的简化?)

于 2014-12-19T19:21:21.277 回答
0

现在可以使用DerivingVia. 首先你定义一个新类型包装器

newtype Describe a = Describe a
 deriving
 newtype (Planet, Lives)

instance (Planet a, Lives a) => Description (Describe a) where
  description :: Describe a -> String
  description a = planet a ++ lives a

Dolphin然后你可以派生一个via的实例Describe Dolphin

{-# Language DerivingVia #-}

newtype Dolphin = Dolphin Cat
 deriving
 newtype Planet

 deriving Description
 via Describe Dolphin

现在:

>> main
"lives on planet earth,lives on land"
"lives on planet earth,lives in the sea"
于 2021-06-26T12:43:14.487 回答