Моя проблема
У меня есть следующее семейство типов, которое отделяет аргументы от функции:
type family
SeparateArgs
( a :: Type )
:: ( Type, [Type] )
where
SeparateArgs (a -> b) =
SndCons2 a (SeparateArgs b)
SeparateArgs a =
'(a, '[])
У меня также есть этот класс типов, чтобы сделать наоборот:
class Refunct args goal newSig | args goal -> newSig where
refunct :: (HList args -> goal) -> newSig
instance Refunct '[] goal goal where
refunct makeA = makeA HNil
instance
( Refunct tailArgs goal c
)
=> Refunct (headArg ': tailArgs) goal (headArg -> c)
where
refunct hFunct x = refunct $ hFunct . (x :>)
Практически каждый раз, когда я использую эти два, я использую их вместе, как показано ниже:
instance
( SeparateArgs a ~ '(goal, args)
, Refunct goal args a
, ...
)
=> ...
Это сделано для того, чтобы я мог прервать аргументы, произвести некоторую обработку, чтобы создать функцию типа HList args -> goal, а затем снова превратить ее в обычную функцию.
Это работает, но это довольно неприятно, поскольку я знаю, что Separate a ~ '(goal, args) => Refunct args goal a, что означает, что требуется только одно из этих утверждений. Однако компилятор не может этого сказать, поэтому мне нужно сообщить об этом.
Это, конечно, не критично, так как мой код сейчас работает, но я бы хотел свести его в один оператор. В идеале, добавив вторую функциональную зависимость к Refunct следующим образом:
class
( SeparateArgs newSig ~ '(goal, args)
)
=> Refunct args goal newSig
| args goal -> newSig
, newSig -> args goal
where
refunct :: (HList args -> goal) -> newSig
(Конечно, это не работает само по себе)
Есть ли способ свести эти два (семейство типов SeparateArgs и класс типов Refunct) в одно ограничение? Я все еще готов определять дополнительные конструкции, я просто хотел бы, чтобы в результате были неизбыточные ограничения. Мне еще понадобится функция refunct.
Если это необходимо, вот моя реализация HList:
data HList (a :: [ Type ]) where
HNil :: HList '[]
(:>) :: a -> HList b -> HList (a ': b)
hHead :: HList (a ': b) -> a
hHead (a :> _) = a
hTail :: HList (a ': b) -> HList b
hTail (_ :> b) = b
Что я пробовал
После обсуждения этого в другом месте было предложено попробовать:
type family
IsAtomic
( a :: Type )
:: Bool
where
IsAtomic (a -> b) = 'False
IsAtomic a = 'True
class
Refunct args goal newSig
| args goal -> newSig
, newSig -> args goal
where
refunct :: (HList args -> goal) -> newSig
instance
( IsAtomic goal ~ 'True
)
=> Refunct '[] goal goal
where
refunct makeA = makeA HNil
instance
( Refunct tailArgs goal c
, IsAtomic (headArg -> c) ~ 'False
)
=> Refunct (headArg ': tailArgs) goal (headArg -> c)
where
refunct hFunct x = refunct $ hFunct . (x :>)
Здесь мы добавляем дополнительное ограничение, согласно которому первый экземпляр работает только если IsAtomic goal ~ 'True, а второй - только если IsAtomic goal ~ 'False, где IsAtomic - это определенное мною семейство типов, которое 'False для функций и 'True для всего остального.
Здесь компилятор, похоже, не может подтвердить, что два экземпляра не нарушают функциональную зависимость. Точная ошибка:
Functional dependencies conflict between instance declarations:
instance (IsAtomic goal ~ 'True) => Refunct '[] goal goal
instance (Refunct tailArgs goal c, IsAtomic goal ~ 'False) =>
Refunct (headArg : tailArgs) goal (headArg -> c)
|
XXX | ( IsAtomic goal ~ 'True
| ^^^^^^^^^^^^^^^^^^^^^^^...
(хорошо, это не совсем так, поскольку я удалил всю идентифицирующую информацию).
Моя интуиция заключается в том, что он не осознает, что IsAtomic goal ~ 'True и IsAtomic goal ~ 'False не могут быть истинными одновременно. Это разумно, поскольку без проверки мы не можем знать, что IsAtomic не является forall a. a, который удовлетворяет обоим ограничениям.
*устарел.import Data.Kindи используйте вместо этогоType. - person HTNW   schedule 21.11.2019