Предположим, что у нас есть класс 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?).Использование пользовательского экземпляра при получении экземпляра через генерализованныйNewtypeDeriving
ВОПРОС: Можно ли получить GHC автоматически получить A
и C
, но использовать нашу собственную заданную реализацию B
при выводе C
?
Например, следующий код (где 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’
Кажется странным, что GHC будет жаловаться на отсутствие instance Lives Dolphin where
если не используя его в (автоматический) вывод Description
для Dolphin
.
'разворачивания Dolphine в Кота, а затем вызвать метод описания на that': Если это происходит, то GHC будет не использовать 'instance Lives Dolphin where' (но вместо этого будет использовать' instance Lives Cat where').Итак, почему ghc жалуется, если этот «экземпляр Lives Dolphin where» отсутствует в первой программе? – artella
'Вопрос: Если определение описания всегда одно и то же, почему он должен быть классом вообще? Почему вы не можете просто определить правильную функцию, которая это делает? »- это просто упрощение ситуации, которую я имел в виду. Фактически я представлял себе сложную иерархию типа-типа и хочу изменить поведение (для данного типа) одного экземпляра в середине иерархии этого типа, не объявляя новый тип и не обновляя все экземпляры, только один экземпляр модифицирован. В идеале я бы только объявил измененный экземпляр. – artella