假设我们有一个类型类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?)。
问题:是否可以让ghc自动派生A
和C
,但是在派生{{B
时使用我们自己指定的C
实现1}}?
例如,以下代码(其中A
= Planet
,B
= Lives
,C
= 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"
我期望/想要的是在Dolphin
的派生中调用Lives
Description
实例。
显然以下程序有效,但需要为Description
显式实例化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]
P.S。令人费解的是,如果(在第一个程序中)我没有声明:
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’
如果instance Lives Dolphin where
Description
Dolphin
的{{1}}的(自动)推导使用它,那么ghc会抱怨{{1}}没有{}}。
答案 0 :(得分: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
的定义始终相同,为什么它必须是一个类?你为什么不能定义一个这样做的常规函数?
(或者这是你要解决的一些更复杂的问题的简化?)
答案 1 :(得分: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
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"