Хаскелл. Как реализовать пользовательский монадный трансформатор с использованием GADT?

#haskell #monad-transformers

#haskell #монадные трансформаторы

Вопрос:

Я пытаюсь узнать о монадных трансформаторах, внедряя свои собственные. Код компилируется, если я использую newType DummyT m x y z = DummyT { runDummyT :: m (Dummy x y z) } , но не если я использую GADT. Однако мне нужно использовать GADT, но это кажется более сложным. Я получаю эту ошибку:

 Expecting one more argument to ‘m’   
Expected a type, but ‘m’ has kind ‘* -> *’                                              
In the first argument ofDummyT’, namely ‘m’                                          
In the first argument ofFunctor’, namely ‘(DummyT m x y)`
  

Я получаю ту же ошибку для экземпляров applicative и monad.
Почему я получаю эту ошибку? Любая помощь приветствуется.

моя монада:

 instance Monad (Dummy x y) where
  return = Return
  (>>=) = Bind

data Dummy x y z where
  Return :: z -> Dummy x y z
  Bind :: Dummy x y z -> (z -> Dummy x y q) -> Dummy x y q
  

попытка преобразования:

 instance Monad m => Functor (DummyT m x y) where
  fmap = liftM

instance Monad m => Applicative (DummyT m x y) where
  pure = return
  (<*>) = ap

instance Monad m => Monad (DummyT m x y) where
  return = Return
  (>>=) = Bind

data DummyT m x y z where
  Return :: z -> DummyT m x y z
  Bind :: DummyT m x y z -> (z -> DummyT m x y q) -> DummyT m x y q
  

Ответ №1:

В

 newType DummyT m x y z = DummyT { runDummyT :: m (Dummy x y z) }
  

очевидно m * -> * , что вид m is применяется к типу.

В

 data DummyT m x y z where
  Return :: z -> DummyT m x y z
  Bind :: DummyT m x y z -> (z -> DummyT m x y q) -> DummyT m x y q
  

m не используется, поэтому GHC выводит kind * , поскольку это самое простое. Возможно, это могло бы сделать это полиподобным, но не сделало.

Если вам нужен другой тип, явно попросите об этом:

 data DummyT (m :: * -> *) x y z where
  Return :: z -> DummyT m x y z
  Bind :: DummyT m x y z -> (z -> DummyT m x y q) -> DummyT m x y q
  

Интересно, однако, имеет ли смысл не использовать m здесь.

Комментарии:

1. * находится на медленном совпадении с забвением. Лучше импортировать Data.Kind и использовать Type .

2. Спасибо, теперь он компилируется. Не могли бы вы объяснить, почему m это может не понадобиться?

3. @datamoose Вы не использовали его в типах Return и Bind , кроме как в качестве аргумента DummyT . Ни в коем случае мы не можем читать m (some type) так, как мы это делали в newtype определении.

4. @datamoose, я подозреваю, что для того, чтобы сделать это монадным преобразователем, вам нужно добавить конструктор Lift :: m a -> DummyT m x y a . Лучше сначала изменить параметры вашего типа : DummyT x y m a . Это позволит вам писать instance MonadTrans (DummyT x y) where lift = Lift .