Рассмотрим следующий код на Haskell (GHC 8.2):
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Constraint
type family Head xs where
Head (x ': xs) = x
type family Tail xs where
Tail (x ': xs) = xs
class IsList xs where
isList :: (xs ~ '[] => r) -> ((xs ~ (Head xs ': Tail xs), IsList (Tail xs)) => r) -> r
instance IsList '[] where isList r _ = r
instance IsList xs => IsList (x ': xs) where isList _ r = r
type family Prepend xs ys where
Prepend '[] ys = ys
Prepend (x ': xs) ys = x ': Prepend xs ys
prependPreservesIsList :: forall xs ys. (IsList xs, IsList ys) => Dict (IsList (Prepend xs ys))
prependPreservesIsList = isList @xs Dict (withDict (prependPreservesIsList @(Tail xs) @ys) Dict)
class IsList (Deps a) => Hard (a :: *) where
type Deps a :: [*]
instance (Hard a, Hard b) => Hard (Either a b) where
type Deps (Either a b) = Prepend (Deps a) (Deps b)
с ошибкой
Main.hs:37:10: error:
• Could not deduce (IsList (Prepend (Deps a) (Deps b)))
arising from the superclasses of an instance declaration
from the context: (Hard a, Hard b)
bound by the instance declaration at Main.hs:37:10-46
• In the instance declaration for ‘Hard (Either a b)’
|
37 | instance (Hard a, Hard b) => Hard (Either a b) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Код пытается создать класс Hard
, который имеетсвязанный список типов Deps
, где Deps
, соответствующий Either a b
, должен быть объединением Deps
, соответствующим a
и b
.
Мы знаем, какдоказать GHC, что эта форма объединения сохраняет класс IsList
, о чем свидетельствует prependPreservesIsList
.Если бы у нас было (Hard a, Hard b)
, и нам нужно было написать нормальный код, который требовал бы (IsList (Deps (Either a b)))
, мы бы просто withDict prependPreservesIsList
и были бы в пути.Но нам нужно, чтобы GHC распознал это ограничение «во время компиляции», чтобы обеспечить допустимость экземпляра Either a b
.
Есть ли способ «открыть словарь ограничений» во время компиляции илив противном случае отменить этот код, чтобы GHC принял экземпляр Either a b
?