Допустим, у нас есть следующее:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilyDependencies #-}
type family CategoryLikeT p_a_b = t | t -> p_a_b
type IsCategoryLike p t a b = (t ~ CategoryLikeT (p, a, b))
class CategoryLike p where
(>>>) ::
(
IsCategoryLike p t1 a b,
IsCategoryLike p t2 b c,
IsCategoryLike p t3 a c
) => t1 -> t2 -> t3
Затем мы обнаруживаем, что это компилируется нормально:
f ::
(
CategoryLike p,
IsCategoryLike p t1 a b,
IsCategoryLike p t2 b c,
IsCategoryLike p t3 c d,
IsCategoryLike p t4 a d
) => t1 -> t2 -> t3 -> t4
f x y z = x >>> y >>> z
Но мы еще не определили ни одного экземпляра. Давайте сделаем это:
data BasicFunction
type instance CategoryLikeT (BasicFunction, a, b) = a -> b
instance CategoryLike BasicFunction where
(>>>) = flip (.)
Но также «Ints» при добавлении являются своего рода категорией, если мы просто предположим, что «a» и «b» оба являются Void
, например: экземпляр типа данных BasicInt CategoryLikeT (BasicInt, Void, Void) = Int
instance CategoryLike BasicFunction where
(>>>) = (+)
Конечно, приведенное выше не работает, потому что в определении экземпляра нет ограничений на «a» или «b», поэтому нет гарантии, что >>>
получит все одинаковые типы, поэтому (+)
не является достаточно общим. Итак, я подумал, что делаю следующее:
Во-первых, добавление типа ограничения:
type family CategoryConstraints p t a b
А затем добавить к определению IsCategoryLike
следующее:
type IsCategoryLike p t a b =
(t ~ CategoryLikeT (p, a, b), CategoryConstraints p t)
Затем мы можем добавить следующее ограничение:
type instance CategoryConstraints BasicInt t = (t ~ Int)
Но теперь у нас есть проблема. f
больше не работает, выдавая эту ошибку:
Could not deduce: CategoryConstraints p (CategoryLikeT (p, a, c)))
Мы можем исправить это двумя способами:
Во-первых, добавив IsCategoryLike p t5 a c
к ограничениям в f
. Но это может быстро запутать более сложные функции, вам придется добавлять ограничение для каждой операции. Также тривиальные изменения, такие как изменение (x >>> y) >>> z
на x >>> (y >>> z)
, требуют изменения подписи, что не требовалось, когда не было ограничений.
В качестве альтернативы сигнатура типа может быть полностью опущена или могут использоваться сигнатуры частичного типа.
Тем не менее, я хотел бы сохранить полные подписи типов, не увеличивая их и не усложняя обслуживание. Могут ли люди предложить альтернативные подходы?