В этот недавний мой ответ, я случайно открыл этот старый каштан (программа такая старая, половина ее была написана в семнадцатом веке Лейбницем и написанный на компьютере в семидесятых моим папой). Я не использую современный бит, чтобы сэкономить место.
class Differentiable f where
type D f :: * -> *
newtype K a x = K a
newtype I x = I x
data (f :+: g) x = L (f x)
| R (g x)
data (f :*: g) x = f x :&: g x
instance Differentiable (K a) where
type D (K a) = K Void
instance Differentiable I where
type D I = K ()
instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where
type D (f :+: g) = D f :+: D g
instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where
type D (f :*: g) = (D f :*: g) :+: (f :*: D g)
Теперь, вот что расстраивает. Я не знаю, как оговаривать, что D f
должен быть дифференцируемым. Конечно, эти случаи уважают это свойство, и могут быть интересные программы, которые вы можете написать, которые используют возможность продолжать дифференцировать функтор, снимать дыры во все большем количестве мест: расширения Тейлора, что-то типа.
Я хотел бы сказать что-то вроде
class Differentiable f where
type D f
instance Differentiable (D f)
и требуют проверки того, что объявления экземпляров имеют определения type
, для которых существуют необходимые экземпляры.
Возможно, более мирские вещи, такие как
class SortContainer c where
type WhatsIn c
instance Ord (WhatsIn c)
...
также будет приятным. Это, конечно, имеет обходное решение
class Ord w => SortContainer c w | c -> w where ...
но попытаться сделать тот же трюк для Differentiable
кажется... ну... инволютивным.
Итак, есть ли замечательное решение, которое позволяет мне повторимую дифференцируемость? (Я думаю, я мог бы создать представление GADT и... и есть ли способ, который работает с классами?)
И есть ли какие-либо очевидные недостатки с предположением, что мы должны будем требовать ограничений на типы (и, я полагаю, данные), когда мы объявляем их, а затем проверяем, удовлетворяют ли им экземпляры?