Фон
Я написал следующий код на Haskell (GHC 8.6.3):
{-# LANGUAGE
NoImplicitPrelude,
MultiParamTypeClasses,
FlexibleInstances, FlexibleContexts,
TypeFamilies, UndecidableInstances,
AllowAmbiguousTypes
#-}
import Prelude(Char, Show, show, undefined, id)
data Nil
nil :: Nil
nil = undefined
instance Show Nil where
show _ = "nil"
data Cons x xs = Cons x xs
deriving Show
class FPack f r where
fpack :: f -> r
instance {-# OVERLAPPABLE #-} f ~ (Nil -> r) => FPack f r where
fpack f = f nil
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) => FPack f (a -> r) where
fpack f a = fpack (\x -> f (Cons a x))
Идея этого кода заключается в создании функции переменной арности, которая принимает ееаргументы и упаковывает их в гетерогенный список.
Например, следующий
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
создает список Cons "a" (Cons "b" nil)
.
Задача
ВВ общем, я хочу вызвать fpack
, передав id
в качестве параметра f
(как выше), поэтому я хочу определить следующую функцию в качестве краткой записи:
pack = fpack id
Если я загружу вышезапрограммируйте в GHCi и выполните указанную выше строку, pack определяется по желанию, а его тип (как указано :t
) равен FPack (a -> a) r => r
.Поэтому я определил функцию в своей программе следующим образом:
pack :: FPack (a -> a) r => r
pack = fpack id
Но при загрузке указанной программы в GHCi возникает следующая ошибка:
bugs\so-pack.hs:31:8: error:
* Overlapping instances for FPack (a0 -> a0) r
arising from a use of `fpack'
Matching givens (or their superclasses):
FPack (a -> a) r
bound by the type signature for:
pack :: forall a r. FPack (a -> a) r => r
at bugs\so-pack.hs:30:1-29
Matching instances:
instance [overlappable] (f ~ (Nil -> r)) => FPack f r
-- Defined at bugs\so-pack.hs:24:31
instance (FPack (x -> b) r, f ~ (Cons a x -> b)) =>
FPack f (a -> r)
-- Defined at bugs\so-pack.hs:27:10
(The choice depends on the instantiation of `a0, r')
* In the expression: fpack id
In an equation for `pack': pack = fpack id
|
31 | pack = fpack id
|
Это приводит меня к моим вопросам.Почему эта функция работает, когда она определена в GHCi, а не в самой программе?Есть ли способ сделать эту работу в самой программе?Если да, то как?
Мои мысли
Из того, что я понимаю о GHC и Haskell, эта ошибка происходит из-за того, что pack
может разрешиться в один из двух перекрывающихся экземпляров, иэто беспокоит GHC.Однако я подумал, что опция AllowAmbiguousTypes
должна решить эту проблему, отложив выбор экземпляра до конечного сайта вызова.К сожалению, этого явно недостаточно.Мне любопытно, почему, но меня еще больше интересует, почему GHCi принимает это определение в цикле REPL, но не принимает его, когда оно находится внутри программы.
Касательный
У меня есть еще один вопрос, касающийся этой программы, который напрямую не связан с основным направлением этого вопроса, но я подумал, что было бы разумно задать его здесь, а не создавать другой вопрос о той же программе.
Как видно изпример выше, т.е.
fpack id "a" "b" :: Cons [Char] (Cons [Char] Nil)
Я должен предоставить явную сигнатуру типа для fpack
, чтобы она работала как нужно.Если я не предоставлю один (т.е. просто позвоню fpack id "a" "b"
), GHCi выдаст следующую ошибку:
<interactive>:120:1: error:
* Couldn't match type `Cons [Char] (Cons [Char] Nil)' with `()'
arising from a use of `it'
* In the first argument of `System.IO.print', namely `it'
In a stmt of an interactive GHCi command: System.IO.print it
Можно ли как-нибудь изменить определение fpack
, чтобы GHC мог вывестиправильная подпись типа?