首页 文章

通过GeneralizedNewtypeDeriving派生实例时使用自定义实例

提问于
浏览
3

假设我们有一个类型类 class (A a, B a) => C a where . 使用 newtype 将允许我们克隆数据类型,然后通过 GeneralizedNewtypeDeriving 语言扩展自动派生实例(请参阅how to write a derivable class?Handling multiple types with the same internal representation and minimal boilerplate?) .

QUESTION :是否有可能让ghc自动派生 AC ,但是在派生 C 时使用我们自己指定的 B 实现?

例如,以下代码(其中 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"

我期待/想要的是 DescriptionDolphin 实例在 Description 的派生中被调用 .

显然以下程序有效,但它需要一个为 Dolphin 显式实例化 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"

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]

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

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会抱怨缺少 instance Lives Dolphin where 如果 notDescriptionDescription (自动)推导中使用 not .

1 回答

  • 2

    考虑以下:

    newtype ProcessID = PID Int deriving Eq
    

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

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

    换句话说,当你在 PID 上调用 == 时,它会将其解包为普通的 Int ,然后对其执行 == .

    我想 deriving instance Description Dolphin 完全一样;将 Dolphine 展开到 Cat 中,然后在其上调用 description 方法 . 哪个不是你想要的!

    问题:如果 description 的定义始终相同,为什么它必须是一个类?你为什么不能定义一个这样做的常规函数?

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

相关问题