Разрешение реализации распределенного DSL выбирать формат сериализации (через семейство ограничений) - PullRequest
1 голос
/ 20 июня 2020

Я пишу DSL для распределенного программирования и хотел бы разрешить реализациям выбирать метод сериализации (если таковой имеется, поскольку он может даже не понадобиться для имитации выполнения).

Попытка решить это добавление семейства типов привело к указанной ниже проблеме для стандартной функции, которая у меня есть. Я полагаю, что это сработало бы, если бы я мог потребовать и чтобы средство проверки типов понимало, что если два значения сериализуемы, их объединение также будет сериализуемым. Однако добавление этого количественного ограничения, похоже, не работает. Можно ли это решить или есть лучшее решение проблемы?

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ConstraintKinds        #-}
{-# LANGUAGE TypeFamilies           #-}

import Data.Kind

class (Monad (DistrM t)) => Distributed (t :: *) where
  type Sendable t :: * -> Constraint
  type DistrM t :: * -> *
  -- ...

data FromSendable t a where
  FromSendable :: (Sendable t b)
               => (b -> DistrM t a)
               -> b
               -> FromSendable t a

pairWith :: ( Sendable t a
            , Distributed t
            , forall a b. (Sendable t a, Sendable t b) => Sendable t (a,b)
            )
         => a
         -> FromSendable t b
         -> FromSendable t (a,b)
pairWith a (FromSendable f b) =
  FromSendable (\(a,b) -> (a,) <$> f b) (a,b)

-- >>> Could not deduce: Sendable t (a1, b1) ...

Edit 1

Тип проверяет, делаю ли я

pairWith :: ( Sendable t a
            , Distributed t
            , st ~ Sendable t
            , forall a b. (st a, st b) => st (a,b)
            )
         => ...

Это станет громоздким повторять эти типы ограничений, поэтому я попробовал использовать синоним типа, но это не сработало:

type Cs t = forall (st :: * -> Constraint).
  (Sendable t ~ st, forall a b. (st a, st b) => st (a,b))
-- >>> Expected a constraint, but ‘st (a, b)’ has kind ‘*’

Ответы [ 2 ]

1 голос
/ 21 июня 2020

Я думаю, что вы выдвинули здесь свой код на грань системы типов GH C. Вы можете исправить подобную ошибку в Cs, написав:

type Cs t = (forall (st :: * -> Constraint).
  (Sendable t ~ st, forall a b. (st a, st b) => st (a,b))) :: Constraint

, но затем вы столкнетесь с тем, что «GH C еще не поддерживает непредикативный полиморфизм». Пока GH C не добавит поддержку семейств классов согласно issue 14860 , вам может не повезти с этим подходом.

Однако вы спрашивали об альтернативных подходах. Не делает ли Sendable t a многопараметрический тип класса свершившимся sh в основном то же самое?

Конечно, следующие проверки типов:

{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE TupleSections          #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE QuantifiedConstraints  #-}
{-# LANGUAGE ConstraintKinds        #-}
{-# LANGUAGE TypeFamilies           #-}

import Data.Kind

class (Monad (DistrM t)) => Distributed (t :: *) where
  type DistrM t :: * -> *
  -- ...
class Sendable t a where

data FromSendable t a where
  FromSendable :: (Sendable t b)
               => (b -> DistrM t a)
               -> b
               -> FromSendable t a

type Cs t = forall a b. (Sendable t a, Sendable t b) => Sendable t (a,b) :: Constraint
pairWith :: ( Sendable t a
            , Distributed t
            , Cs t
            )
         => a
         -> FromSendable t b
         -> FromSendable t (a,b)
pairWith a (FromSendable f b) =
  FromSendable (\(a,b) -> (a,) <$> f b) (a,b)
1 голос
/ 21 июня 2020

Это выглядит странно. У меня есть только частичный ответ, но я все равно отправлю его. Я упростил ваш код до

class C t where   -- (*)

data T t where
  T :: C t => (a -> t) -> a -> T t

foo ::
   ( C u
   , forall a b . (C a , C b) => C (a, b) )
  => u -> T t -> T (u, t)
foo i (T f x) = T (\(a,b) -> (a, f b)) (i, x)

, и в этой версии он компилируется нормально. Однако, если мы заменим

class C t where

на

type instance C :: * -> Constraint

, мы получим ошибку, сообщающую нам, что C (a, b) не может быть выведено.

Я не могу полностью понять, что здесь происходит, но похоже, что количественно определенные ограничения плохо сочетаются с семействами типов.

Похоже, что указанное выше семейство типов рассматривается как

type instance C (t :: *) :: Constraint

и в такой случай, я не могу понять в чем дело. Поскольку C теперь не относится к классу одного типа, невозможно реализовать количественное ограничение, такое как forall a b . (C a , C b) => C (a, b), (скажем) передав указатель на конкретный экземпляр c, поскольку три ограничения C могут быть что угодно, в открытом мире.

Я до сих пор не понимаю, почему type family C :: * -> Constraint обрабатывается таким же образом.

Возможно, GH C должен отвергать количественные ограничения, связанные с семействами типов ... -> Constraint таким образом? Я не уверен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...