Использование прокси в TypeClass

Я пытаюсь сделать что-то похожее на класс Wrapper f, где wrap :: a -> fa unwrap :: fa -> имя :: Proxy ( fa ) -> String = "https://stackoverflow.com/questions/13629538 / a-way-to-declare-a-constant-value-in-type-class "> этот вопрос .

Я хотел бы определить тип

instance (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

и затем определите

Could not deduce (Wrapper f0) arising from a use of a€?namea€™                                               
      from the context: (IsString a, FromJSON a, Wrapper f)                                                    
        bound by the instance declaration at src/Model/Wrapper.hs:29:10-62                                     
      The type variable a€?f0a€™ is ambiguous

Но я получаю сообщение об ошибке

f a

Мне непонятно, почему это не работает, и если можно как-то это исправить

haskell,typeclass,

1

Ответов: 1


9 принят

Сначала несколько замечаний:

  1. Не указывайте такой экземпляр . Этот экземпляр будет соответствовать любому формату f, независимо от того f, действительно ли он в Wrapperклассе. В частности, это также противоречило бы со стандартными экземплярами, такими как FromJSON (Vector a), даже если Vector это не может быть (хороший) экземпляр Wrapper. Причиной этого является то, что система классов Haskell основана на предположении открытого мира : компилятор никогда не может предположить, что тип не находится в каком-то классе, потому что любой мог, по крайней мере технически говоря, добавить экземпляр позже.
  2. Я бы советовал не использовать Proxyв новом коде. Я всегда считал Proxyуродливый хак, едва менее уродливым, чем undefined :: Tаргументы, которые обычно использовались для этого в старом кодеке Haskell. В новом GHC, то проблема была решена должным образом -XAllowAmbiguousTypesс -XTypeApplications; они позволяют просто сделать подпись

    {-# LANGUAGE AllowAmbiguousTypes #-}
    class Wrapper f where
      ...
      name :: String

    а затем вместо name (Proxy :: Proxy (f a))записи name @f.

Теперь к реальной проблеме: ваш код не работает, потому что переменные типа в стандартном Haskell всегда принадлежат только контексту подписи / класса одного типа, но не могут использоваться в коде, который его определяет. IOW, переменные типа не используют те же области имен , что и переменные значения, поэтому, когда вы упоминаете Proxy (f a)компилятор a € ?disambiguatesa €? переменные типа to 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

Никаких занятий не требуется.

Haskell, класс типов,
Похожие вопросы
Яндекс.Метрика