Внедрение индексированного функтора в копроизведение функтора

Пытаюсь работать с индексированной бесплатной монадой (у Олега Киселева есть вступление). Я также хочу, чтобы эта свободная монада была построена из побочного продукта функторов а-ля Типы данных по выбору. Тем не менее, у меня возникли проблемы с тем, чтобы класс типа впрыска попутного продукта работал. Вот что у меня есть до сих пор:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Example where

import Control.Monad.Indexed
import Data.Kind
import Data.TASequence.FastCatQueue
import Prelude hiding ((>>), (>>=))

-- * Indexed free machinery

-- For use with `RebindableSyntax`
(>>=)
  :: (IxMonad m)
  => m s1 s2 a -> (a -> m s2 s3 b) -> m s1 s3 b
(>>=) = (>>>=)
(>>)
  :: (IxApplicative m)
  => m s1 s2 a -> m s2 s3 b -> m s1 s3 b
f >> g = imap (const id) f `iap` g

type family Fst x where
  Fst '(a, b) = a
type family Snd x where
  Snd '(a, b) = b

newtype IKleisliTupled m ia ob = IKleisliTupled
  { runIKleisliTupled :: Snd ia -> m (Fst ia) (Fst ob) (Snd ob)
  }

data Free f s1 s2 a where
  Pure :: a -> Free f s s a
  Impure ::
    f s1 s2 a ->
      FastTCQueue (IKleisliTupled (Free f)) '(s2, a) '(s3, b) ->
        Free f s1 s3 b

instance IxFunctor (Free f) where
  imap f (Pure a) = Pure $ f a
  imap f (Impure a g) = Impure a (g |> IKleisliTupled (Pure . f))
instance IxPointed (Free f) where
  ireturn = Pure
instance IxApplicative (Free f) where
  iap (Pure f) (Pure a) = ireturn $ f a
  iap (Pure f) (Impure a g) = Impure a (g |> IKleisliTupled (Pure . f))
  iap (Impure a f) m = Impure a (f |> IKleisliTupled (`imap` m))
instance IxMonad (Free f) where
  ibind f (Pure a) = f a
  ibind f (Impure a g) = Impure a (g |> IKleisliTupled f)

-- * Example application

data FileStatus
  = FileOpen
  | FileClosed
data File i o a where
  Open :: FilePath -> File 'FileClosed 'FileOpen ()
  Close :: File 'FileOpen 'FileClosed ()
  Read :: File 'FileOpen 'FileOpen String
  Write :: String -> File 'FileOpen 'FileOpen ()

data MayFoo
  = YesFoo
  | NoFoo
data Foo i o a where
  Foo :: Foo 'NoFoo 'YesFoo ()

data MayBar
  = YesBar
  | NoBar
data Bar i o a where
  Bar :: Bar 'YesBar 'NoBar ()

-- * Coproduct of indexed functors

infixr 5 `SumP`
data SumP f1 f2 t1 t2 x where
  LP :: f1 sl1 sl2 x -> SumP f1 f2 '(sl1, sr) '(sl2, sr) x
  RP :: f2 sr1 sr2 x -> SumP f1 f2 '(sl, sr1) '(sl, sr2) x

-- * Attempt 1

class Inject l b where
  inj :: forall a. l a -> b a

instance Inject (f i o) (f i o) where
  inj = id

instance Inject (fl il ol) (SumP fl fr '(il, s) '(ol, s)) where
  inj = LP

instance (Inject (f i' o') (fr is os)) =>
         Inject (f i' o') (SumP fl fr '(s, is) '(s, os)) where
  inj = RP . inj

send
  :: Inject (t i o) (f is os)
  => t i o b -> Free f is os b
send t = Impure (inj t) (tsingleton (IKleisliTupled Pure))

-- Could not deduce `(Inject (Bar 'YesBar 'NoBar) f s30 s40)`
prog
  :: (Inject (File 'FileClosed 'FileOpen) (f s1 s2)
     ,Inject (Foo 'NoFoo 'YesFoo) (f s2 s3)
     ,Inject (Bar 'YesBar 'NoBar) (f s3 s4)
     ,Inject (File 'FileOpen 'FileClosed) (f s4 s5))
  => Free f s1 s5 ()
prog = do
  send (Open "/tmp/foo.txt")
  x <- send Read
  send Foo
  send (Write x)
  send Bar
  send Close

-- * Attempt 2

bsend :: (t i o b -> g is os b) -> t i o b -> Free g is os b
bsend f t = Impure (f t) (tsingleton (IKleisliTupled Pure))

-- Straightforward but not very usable

bprog
  ::
    Free
      (File `SumP` Bar `SumP` Foo)
      '( 'FileClosed, '( 'YesBar, 'NoFoo))
      '( 'FileClosed, '( 'NoBar, 'YesFoo))
      ()
bprog = do
  bsend LP (Open "/tmp/foo.txt")
  x <- bsend LP Read
  bsend (RP . RP) Foo
  bsend (RP . LP) Bar
  bsend LP (Write x)
  bsend LP Close

-- * Attempt 3

class Inject' f i o (fs :: j -> j -> * -> *) where
  type I f i o fs :: j
  type O f i o fs :: j
  inj' :: forall x. f i o x -> fs (I f i o fs) (O f i o fs) x

instance Inject' f i o f where
  type I f i o f = i
  type O f i o f = o
  inj' = id

-- Illegal polymorphic type: forall (s :: k1). '(il, s)

instance Inject' fl il ol (SumP fl fr) where
  type I fl il ol (SumP fl fr) = forall s. '(il, s)
  type O fl il ol (SumP fl fr) = forall s. '(ol, s)
  inj' = LP

instance (Inject' f i' o' fr) =>
         Inject' f i' o' (SumP fl fr) where
  type I f i' o' (SumP fl fr) = forall s. '(s, I f i' o' fr)
  type O f i' o' (SumP fl fr) = forall s. '(s, O f i' o' fr)
  inj' = RP . inj

Таким образом, Попытка 1 разрушает вывод типа. Попытка 2 имеет плохую эргономику для пользователя. Попытка 3 кажется правильным подходом, но я не могу понять, как заставить экземпляры связанного типа работать. Как должна выглядеть эта инъекция?


person JesseC    schedule 25.11.2016    source источник


Ответы (2)


Сначала я представлю стандартный в настоящее время способ обработки открытых сумм. Я делаю это для простых неиндексированных функторов для простоты и потому, что конструкция для индексированных одинакова. Затем я представлю некоторые усовершенствования, реализованные в GHC 8.

Во-первых, мы определяем n-арные суммы функторов как GADT, индексированные списком функторов. Это удобнее и чище, чем использование двоичных сумм.

{-# language
  RebindableSyntax, TypeInType, TypeApplications,
  AllowAmbiguousTypes, GADTs, TypeFamilies, ScopedTypeVariables,
  UndecidableInstances, LambdaCase, EmptyCase, TypeOperators, ConstraintKinds,
  FlexibleContexts, MultiParamTypeClasses, FlexibleInstances #-}

import Data.Kind

data NS :: [* -> *] -> * -> * where
  Here  :: f x -> NS (f ': fs) x
  There :: NS fs x -> NS (f ': fs) x

instance Functor (NS '[]) where
  fmap _ = \case {}

instance (Functor f, Functor (NS fs)) => Functor (NS (f ': fs)) where
  fmap f (Here fx)  = Here  (fmap f fx)
  fmap f (There ns) = There (fmap f ns)

Проектирование и инъекция могут быть выполнены либо

  • Непосредственно с классом, но для этого нужны перекрывающиеся или несогласованные экземпляры.
  • Косвенно, сначала вычисляя индекс элемента, в который мы хотели бы внедрить, а затем используя индекс (натуральное число) для определения экземпляров класса без перекрытия.

Последнее решение является предпочтительным, поэтому давайте посмотрим, что:

data Nat = Z | S Nat

type family Find (x :: a) (xs :: [a]) :: Nat where
  Find x (x ': xs) = Z
  Find x (y ': xs) = S (Find x xs)

class Elem' (n :: Nat) (f :: * -> *) (fs :: [* -> *]) where
  inj' :: forall x. f x -> NS fs x
  prj' :: forall x. NS fs x -> Maybe (f x)

instance (gs ~ (f ': gs')) => Elem' Z f gs where
  inj'           = Here
  prj' (Here fx) = Just fx
  prj' _         = Nothing

instance (Elem' n f gs', (gs ~ (g ': gs'))) => Elem' (S n) f gs where
  inj'            = There . inj' @n
  prj' (Here _)   = Nothing
  prj' (There ns) = prj' @n ns

type Elem f fs = (Functor (NS fs), Elem' (Find f fs) f fs)  

inj :: forall fs f x. Elem f fs => f x -> NS fs x
inj = inj' @(Find f fs)

prj :: forall f x fs. Elem f fs => NS fs x -> Maybe (f x)
prj = prj' @(Find f fs)

Сейчас в ghci:

> :t inj @[Maybe, []] (Just True)
inj @[Maybe, []] (Just True) :: NS '[Maybe, []] Bool

Однако наше семейство типов Find несколько проблематично, поскольку его сокращение часто блокируется переменными типа. GHC запрещает ветвление на основе неравенства типовых переменных, потому что унификация может привести к тому, что различные переменные позже будут преобразованы в одинаковые типы (и принятие преждевременных решений на основе неравенства может привести к потере решений).

Например:

> :kind! Find Maybe [Maybe, []]
Find Maybe [Maybe, []] :: Nat
= 'Z  -- this works
> :kind! forall (a :: *)(b :: *). Find (Either b) [Either a, Either b]
forall (a :: *)(b :: *). Find (Either b) [Either a, Either b] :: Nat
= Find (Either b) '[Either a, Either b] -- this doesn't

Во втором примере GHC не учитывает неравенство a и b, поэтому он не может перешагнуть через первый элемент списка.

Это исторически вызывало довольно раздражающие недостатки вывода типов в типах данных a la Carte и расширяемых библиотеках эффектов. Например, даже если у нас есть только один эффект State s в сумме функторов, запись (x :: n) <- get в контексте, где известен только Num n, приводит к ошибке вывода типа, потому что GHC не может вычислить индекс эффекта State, когда параметр состояния равен переменная типа.

Однако с помощью GHC 8 мы можем написать значительно более мощное семейство типов Find, которое просматривает выражения типов, чтобы увидеть, существует ли уникальный возможный индекс позиции. Например, если мы пытаемся найти эффект State s, если в списке эффектов есть только один State, мы можем безопасно вернуть его позицию, не глядя на параметр s, и впоследствии GHC сможет унифицировать s с другим параметр состояния, содержащийся в списке.

Во-первых, нам нужен общий обход выражений типа:

import Data.Type.Bool

data Entry = App | forall a. Con a

type family (xs :: [a]) ++ (ys :: [a]) :: [a] where
  '[]       ++ ys = ys
  (x ': xs) ++ ys = x ': (xs ++ ys)

type family Preord (x :: a) :: [Entry] where
  Preord (f x) = App ': (Preord f ++ Preord x)
  Preord x     = '[ Con x]

Preord преобразует произвольный тип в список его подвыражений в предварительном порядке. App обозначает места, где происходит применение конструктора типов. Например:

> :kind! Preord (Maybe Int)
Preord (Maybe Int) :: [Entry]
= '['App, 'Con Maybe, 'Con Int]
> :kind! Preord [Either String, Maybe]
Preord [Either String, Maybe] :: [Entry]
= '['App, 'App, 'Con (':), 'App, 'Con Either, 'App, 'Con [],
   'Con Char, 'App, 'App, 'Con (':), 'Con Maybe, 'Con '[]]

После этого написание нового Find — это просто вопрос функционального программирования. Моя реализация, приведенная ниже, преобразует список типов в список пар индекс-обход и последовательно отфильтровывает записи из списка, сравнивая обходы элементов списка и элемента, который нужно найти.

type family (x :: a) == (y :: b) :: Bool where
  x == x = True
  _ == _ = False

type family PreordList (xs :: [a]) (i :: Nat) :: [(Nat, [Entry])] where
  PreordList '[]       _ = '[]
  PreordList (a ': as) i = '(i, Preord a) ': PreordList as (S i)

type family Narrow (e :: Entry) (xs :: [(Nat, [Entry])]) :: [(Nat, [Entry])] where
  Narrow _ '[]                     = '[]
  Narrow e ('(i, e' ': es) ': ess) = If (e == e') '[ '(i, es)] '[] ++ Narrow e ess

type family Find_ (es :: [Entry]) (ess :: [(Nat, [Entry])]) :: Nat where
  Find_ _        '[ '(i, _)] = i
  Find_ (e ': es) ess        = Find_ es (Narrow e ess)

type Find x ys = Find_ (Preord x) (PreordList ys Z)

Теперь у нас есть:

> :kind! forall (a :: *)(b :: *). Find (Either a) [Maybe, [], Either b]
forall (a :: *)(b :: *). Find (Either a) [Maybe, [], Either b] :: Nat
= 'S ('S 'Z)

Этот Find можно использовать в любом коде, использующем открытые суммы, и он одинаково работает для индексированных и неиндексированных типов.

Вот пример кода с видом инъекция/проекция, представленная выше, для неиндексируемых расширяемых эффектов.

person András Kovács    schedule 25.11.2016
comment
Спасибо! Я знаком с вашим NS. Проблема в том, что расширение его для работы с индексированными функторами для меня неочевидно. data NS (fs :: [* -> *]) is os (a :: *) Какими должны быть типы is и os (если мы хотим поддерживать индексированные функторы, где каждый функтор может быть проиндексирован типами другого типа)? Список типов? Список открытых союзов? - person JesseC; 26.11.2016
comment
Кстати, экземпляр Functor (NS fs) можно указать напрямую, утверждая, что все fs являются функторами: lpaste.net/ 5782847892658061312 - person dfeuer; 26.11.2016
comment
Не могли бы вы объяснить, как это помогает дать обход типа, а не просто дерево, представляющее его или сам тип? - person dfeuer; 26.11.2016
comment
@JesseC Хорошо, теперь я понимаю, что вы хотите получить сопутствующие произведения индексированных функторов, которые индексируются произведением индексов. Это действительно требует дополнительной работы. Я отредактирую ответ. - person András Kovács; 26.11.2016
comment
@dfeuer Идя дальше, мы можем использовать StandaloneDeriving, чтобы избежать записи экземпляра — deriving instance All Functor fs => Functor (NS fs) - person Iceland_jack; 26.11.2016
comment
@ AndrásKovács Извините за немного OT-вопрос, но мне интересно, полезно ли это при использовании для расширяемых эффектов. Разве большинство функций не имеют тип со списком полиморфных эффектов, чтобы их можно было комбинировать? Помогает ли этот трюк в выводе, когда ваш тип что-то вроде Elem (State s) effs => Eff effs () вместо Eff [State s] ()? - person Luka Horvat; 26.11.2016
comment
@LukaHorvat решает все проблемы вывода типов с помощью расширяемых эффектов по сравнению с mtl (а также поддерживает несколько эффектов одного типа и помеченных эффектов). См., например, это. Это, вероятно, стоит написать или запросить пулл-реквест. Я еще не удосужился его как-то продвигать. - person András Kovács; 26.11.2016
comment
@ AndrásKovács Я пробовал это f :: (Num s, Elem (State s) effs) => Eff effs () f = do { put 5; modify (+1) }, и это не делает выводов. Ваши примеры работают, потому что функция run помещает эффекты в конкретный список. Я надеялся, что то, что вы показали, каким-то волшебным образом поможет и в этих случаях. - person Luka Horvat; 26.11.2016
comment
Виды могут быть полиморфными (NS :: [ĸ -> Type] -> (ĸ -> Type) — и — Elem' :: Nat -> (ĸ -> Type) -> [ĸ -> Type] -> Constraint). inj', prj' могут быть объединены в Prism' (NS fs x) (f x), а семейство типов == доступно из Data.Type.Equality. - person Iceland_jack; 26.11.2016
comment
@LukaHorvat: удалите аннотацию типа, тогда она будет работать с более общим типом. Общая ошибка/ограничение GHC не позволяет нам правильно аннотировать этот пример. - person András Kovács; 26.11.2016
comment
@Iceland_jack: Data.Type.Equality равенство однородно, поэтому в данном случае не работает. - person András Kovács; 26.11.2016
comment
Да заметил, что после того, как я написал - person Iceland_jack; 26.11.2016
comment
@ AndrásKovács Да, это так, но только с включенным AllowAmbiguousTypes. Это вообще нормально? - person Luka Horvat; 26.11.2016
comment
@LukaHorvat AllowAmbiguousTypes в порядке и необходим с TypeApplications. Однако, чтобы быть ясным, я думаю, что производительность проверки типов GHC и сообщения об ошибках недостаточно хороши, чтобы сделать эту вещь практичной. Я не думаю, что расширяемые эффекты особенно хороши на практике. - person András Kovács; 26.11.2016
comment
@JesseC: вот некоторый код с индексацией так, как вы хотели бы. Это включает поднятые гетерогенные списки. К сожалению, ошибки GHC помешали мне написать интерпретатор эффектов. Кажется, это выходит за рамки вещей, которые можно написать с помощью GHC, сохраняя при этом наше здравомыслие. - person András Kovács; 26.11.2016
comment
О, теперь я понимаю, я думаю. Сначала вы хотите сопоставить самую внешнюю часть самой внешней части и т. Д. И только сузить, пока не дойдете до одного. Я думаю, что это правильно для определенных целей, хотя, конечно, не для всех. - person dfeuer; 26.11.2016
comment
@dfeuer вопрос заключается в том, к какому обходу следует выполнить предварительную фиксацию, учитывая, что встреча с любой переменной типа на пути заставляет нас застрять. Как правило, конструкторы внешних типов менее полиморфны. Предварительный заказ bfs и dfs кажутся лучшими, причем bfs, вероятно, лучше, но проблема с bfs заключается в том, что он всегда зависает, если в начале корешка приложения есть переменная. Поэтому я выбрал предзаказ dfs. Для реалистичных вариантов использования (расширяемые эффекты и т. д.) мы редко доходим до момента, когда dfs и bfs имеют значение. - person András Kovács; 26.11.2016
comment
@dfeuer ofc, идеальным был бы полный доступ к структуре терминов, включая переменные, аналогично отражению в Agda и Coq. - person András Kovács; 26.11.2016
comment
@ AndrásKovács, в каком дереве вы говорите о BFS? Дерево, которое кажется мне наиболее естественным, — это data Ent where Ap :: a -> [Ent] -> Ent; Co :: a -> Ent, которое представляет f a b как Ap f [Co a, Co b]. Это тот, о котором вы говорите? - person dfeuer; 27.11.2016
comment
@dfeuer да, вот и все. - person András Kovács; 27.11.2016

Ага, у меня получилось! Ключевой момент, который я взял из второй попытки Андраша Ковача (ссылка в комментарии), — это трюк с оставлением головы экземпляра в общем, а затем уточнением с ограничением равенства.

{-# LANGUAGE FlexibleInstances, GADTs, MultiParamTypeClasses,
  RankNTypes, RebindableSyntax, TypeFamilies, TypeInType,
  TypeOperators, UndecidableInstances #-}

module Example2 (res, prog') where

import Control.Monad.Indexed
import Data.TASequence.FastCatQueue
import Prelude hiding ((>>), (>>=))

-- * Indexed free machinery

(>>=)
  :: (IxMonad m)
  => m s1 s2 a -> (a -> m s2 s3 b) -> m s1 s3 b
(>>=) = (>>>=)
(>>)
  :: (IxApplicative m)
  => m s1 s2 a -> m s2 s3 b -> m s1 s3 b
f >> g = imap (const id) f `iap` g

type family Fst x where
  Fst '(a, b) = a
type family Snd x where
  Snd '(a, b) = b

newtype IKleisliTupled m ia ob = IKleisliTupled
  { runIKleisliTupled :: Snd ia -> m (Fst ia) (Fst ob) (Snd ob)
  }

tApp :: (TASequence s, IxMonad m) => s (IKleisliTupled m) x y -> (IKleisliTupled m) x y
tApp fs =
  case tviewl fs of
    TAEmptyL -> IKleisliTupled ireturn
    f :< fs' ->
      IKleisliTupled
        (\a -> runIKleisliTupled f a >>= runIKleisliTupled (tApp fs'))

data Free f s1 s2 a where
  Pure :: a -> Free f s s a
  Impure ::
    f s1 s2 a ->
      FastTCQueue (IKleisliTupled (Free f)) '(s2, a) '(s3, b) ->
        Free f s1 s3 b

instance IxFunctor (Free f) where
  imap f (Pure a) = Pure $ f a
  imap f (Impure a g) = Impure a (g |> IKleisliTupled (Pure . f))
instance IxPointed (Free f) where
  ireturn = Pure
instance IxApplicative (Free f) where
  iap (Pure f) (Pure a) = ireturn $ f a
  iap (Pure f) (Impure a g) = Impure a (g |> IKleisliTupled (Pure . f))
  iap (Impure a f) m = Impure a (f |> IKleisliTupled (`imap` m))
instance IxMonad (Free f) where
  ibind f (Pure a) = f a
  ibind f (Impure a g) = Impure a (g |> IKleisliTupled f)

-- * Example application

data FileStatus
  = FileOpen
  | FileClosed
data File i o a where
  Open :: FilePath -> File 'FileClosed 'FileOpen ()
  Close :: File 'FileOpen 'FileClosed ()
  Read :: File 'FileOpen 'FileOpen String
  Write :: String -> File 'FileOpen 'FileOpen ()

foldFile :: File i o a -> a
foldFile (Open _) = ()
foldFile Close = ()
foldFile Read = "demo"
foldFile (Write _) = ()

data MayFoo
  = YesFoo
  | NoFoo
data Foo i o a where
  Foo :: Foo 'NoFoo 'YesFoo ()

data MayBar
  = YesBar
  | NoBar
data Bar i o a where
  Bar :: Bar 'YesBar 'NoBar ()

-- * Coproduct of indexed functors

infixr 5 `SumP`
data SumP f1 f2 t1 t2 x where
  LP :: f1 sl1 sl2 x -> SumP f1 f2 '(sl1, sr) '(sl2, sr) x
  RP :: f2 sr1 sr2 x -> SumP f1 f2 '(sl, sr1) '(sl, sr2) x

newtype VoidFunctor is os a = VoidFunctor (VoidFunctor is os a)
absurd :: VoidFunctor is os a -> b
absurd a = a `seq` spin a where
   spin (VoidFunctor b) = spin b

extract :: Free VoidFunctor '() '() a -> a
extract (Pure a) = a
extract (Impure f _) = absurd f

runPure
  :: (forall j p b. f j p b -> b)
  -> Free (f `SumP` fs) '(i, is) '(o, os) a
  -> Free fs is os a
runPure _ (Pure a) = Pure a
runPure f (Impure (RP cmd) q) = Impure cmd (tsingleton k)
  where k = IKleisliTupled $ \a -> runPure f $ runIKleisliTupled (tApp q) a
runPure f (Impure (LP cmd) q) = runPure f $ runIKleisliTupled (tApp q) (f cmd)

-- * Injection

class Inject l b where
  inj :: forall a. l a -> b a

instance Inject (f i o) (f i o) where
  inj = id

instance {-# OVERLAPPING #-}
  (is ~ '(il, s), os ~ '(ol, s)) =>
  Inject (fl il ol) (SumP fl fr is os) where
  inj = LP

instance (Inject (f i' o') (fr is' os'), is ~ '(s, is'), os ~ '(s, os')) =>
         Inject (f i' o') (SumP fl fr is os) where
  inj = RP . inj

send
  :: Inject (t i o) (f is os)
  => t i o b -> Free f is os b
send t = Impure (inj t) (tsingleton (IKleisliTupled Pure))

-- * In use

prog = do
  send (Open "/tmp/foo.txt")
  x <- send Read
  send Foo
  send (Write x)
  send Bar
  send Close
  ireturn x

prog' ::
  Free
    (File `SumP` Foo `SumP` Bar `SumP` VoidFunctor)
    '( 'FileClosed, '( 'NoFoo, '( 'YesBar, '())))
    '( 'FileClosed, '( 'YesFoo, '( 'NoBar, '())))
    String
prog' = prog

res :: String
res = extract . runPure (\Bar -> ()) . runPure (\Foo -> ()) . runPure foldFile $ prog

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

person JesseC    schedule 27.11.2016
comment
Тем временем я обнаружил проблему с моим кодом, которая была моей ошибкой, а не ошибкой GHC (просто перепутал индексы в ограничениях). Теперь все работает нормально, вот код. - person András Kovács; 27.11.2016
comment
@AndrásKovács О, интересно. Наконец-то я снова посмотрел на это и столкнулся с неудобством. Если вы не хотите, чтобы объединение было конкретным, что важно для композиционности, вам придется использовать первый подход здесь: lpaste.net/357141 Похоже, вы должны уметь делать только второе. Можете ли вы придумать способ кодирования, чтобы второе стало возможным? - person JesseC; 22.07.2017
comment
@ AndrásKovács Другая проблема, с которой я сталкиваюсь, пытаясь написать runFile. Я могу заставить его работать, но только с помощью unsafeCoerce. Есть ли способ избежать этого? lpaste.net/357146 - person JesseC; 22.07.2017
comment
Я посмотрю на это. Вы можете прочитать это, если вы еще этого не сделали. подсказки; это библиотека в Идрисе с расширенным набором наших функций (на самом деле гораздо более мощных). - person András Kovács; 23.07.2017
comment
Хотя я не очень мотивирован. Нам нужны правильные доказательства и/или размышления и поиск доказательств, которые отсутствуют или дерьмовые в Haskell. напр. нам нужен этот (Elem x (y : ys) ∧ (x /= y) => Elem x ys) или этот (forall ys. Elem x xs => Elem x (xs ++ ys)) и так далее. - person András Kovács; 23.07.2017
comment
Да. Я также наткнулся на okmij.org/ftp/Haskell/extensible/ParamEff1.hs что актуально, но не затрагивает ни одну из двух существующих проблем. - person JesseC; 23.07.2017