Сначала пара замечаний:
- Не определяйте такой экземпляр . Этот экземпляр будет соответствовать что угодно формы
f a
, независимо от того, действительно ли f
находится в классе Wrapper
. В частности, это также может конфликтовать со стандартными экземплярами, такими как FromJSON (Vector a)
, хотя Vector
не может быть (хорошо себя ведущими) экземпляром Wrapper
. Причина этого заключается в том, что система классов типов Haskell основана на предположении открытого мира : компилятор никогда не может предположить, что тип не принадлежит какому-либо классу, потому что кто-либо может, по крайней мере с технической точки зрения, добавить экземпляр позже.
Я бы не советовал использовать Proxy
в новом коде. Я всегда считал Proxy
уродливым хаком, чуть менее уродливым, чем undefined :: T
аргументы, которые обычно использовались для этого в старом коде на Haskell. В новом GHC проблема была исправлена правильно -XAllowAmbiguousTypes
с -XTypeApplications
; они позволяют просто сделать подпись
{-# LANGUAGE AllowAmbiguousTypes #-}
class Wrapper f where
...
name :: String
и затем вместо name (Proxy :: Proxy (f a))
только запись name @f
.
Теперь к актуальной проблеме: ваш код не работает, потому что переменные типа в стандартном Haskell всегда принадлежат только к сигнатуре / контексту одного типа, но не могут использоваться в коде, который его определяет. Итак, переменные типа не используют те же области имен , что и переменные-значения, поэтому, когда вы упоминаете Proxy (f a)
, компилятор «устраняет неоднозначность» переменных типа с f0
и a0
. Это немного глупый недостаток Haskell98 и устраняется расширением -XScopedTypeVariables
(вместе с ключевым словом ∀
aka forall
). Следующее скомпилирует само по себе:
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}
instance ∀ f a . (IsString a, FromJSON a, Wrapper f) => FromJSON (f a) where
parseJSON (String s) = wrap <$> pure (fromString $ unpack s)
parseJSON invalid = typeMismatch (name (Proxy :: Proxy (f a))) invalid
Как я уже сказал, такой экземпляр не должен быть определен. Я думаю, что вы на самом деле хотите что-то вроде
{-# LANGUAGE DataKinds, KindSignatures, TypeApplications #-}
import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)
data Wrapper (n :: String) (a :: *)
= Wrapper a
| TypeMismatch String
instance ∀ a s . (IsString a, FromJSON a, KnownSymbol s)
=> FromJSON (Wrapper s a) where
parseJSON (String s) = Wrapper <$> pure (fromString $ unpack s)
parseJSON invalid = TypeMismatch $ symbolVal @s Proxy
Занятия не требуются.